home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkCanvas.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-19  |  100.2 KB  |  3,393 lines

  1. /* 
  2.  * tkCanvas.c --
  3.  *
  4.  *    This module implements canvas widgets for the Tk toolkit.
  5.  *    A canvas displays a background and a collection of graphical
  6.  *    objects such as rectangles, lines, and texts.
  7.  *
  8.  * Copyright 1991-1992 Regents of the University of California.
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvas.c,v 1.28 92/08/19 08:47:57 ouster Exp $ SPRITE (Berkeley)";
  20. #endif
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include "default.h"
  26. #include "tkInt.h"
  27. #include "tkConfig.h"
  28. #include "tkCanvas.h"
  29.  
  30. /*
  31.  * See tkCanvas.h for key data structures used to implement canvases.
  32.  */
  33.  
  34. /*
  35.  * The structure defined below is used to keep track of a tag search
  36.  * in progress.  Only the "prevPtr" field should be accessed by anyone
  37.  * other than StartTagSearch and NextItem.
  38.  */
  39.  
  40. typedef struct TagSearch {
  41.     Tk_Canvas *canvasPtr;    /* Canvas widget being searched. */
  42.     Tk_Uid tag;            /* Tag to search for.   0 means return
  43.                  * all items. */
  44.     Tk_Item *prevPtr;        /* Item just before last one found (or NULL
  45.                  * if last one found was first in the item
  46.                  * list of canvasPtr). */
  47.     Tk_Item *currentPtr;    /* Pointer to last item returned. */
  48.     int searchOver;        /* Non-zero means NextItem should always
  49.                  * return NULL. */
  50. } TagSearch;
  51.  
  52. /*
  53.  * Information used for argv parsing.
  54.  */
  55.  
  56.  
  57. static Tk_ConfigSpec configSpecs[] = {
  58.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  59.     DEF_CANVAS_BG_COLOR, Tk_Offset(Tk_Canvas, bgBorder),
  60.     TK_CONFIG_COLOR_ONLY},
  61.     {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL,
  62.     (char *) NULL, Tk_Offset(Tk_Canvas, bgColor),
  63.     TK_CONFIG_COLOR_ONLY},
  64.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  65.     DEF_CANVAS_BG_MONO, Tk_Offset(Tk_Canvas, bgBorder),
  66.     TK_CONFIG_MONO_ONLY},
  67.     {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL,
  68.     (char *) NULL, Tk_Offset(Tk_Canvas, bgColor),
  69.     TK_CONFIG_MONO_ONLY},
  70.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  71.     (char *) NULL, 0, 0},
  72.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  73.     (char *) NULL, 0, 0},
  74.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  75.     DEF_CANVAS_BORDER_WIDTH, Tk_Offset(Tk_Canvas, borderWidth), 0},
  76.     {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
  77.     DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(Tk_Canvas, closeEnough), 0},
  78.     {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
  79.     DEF_CANVAS_CONFINE, Tk_Offset(Tk_Canvas, confine), 0},
  80.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  81.     DEF_CANVAS_CURSOR, Tk_Offset(Tk_Canvas, cursor), TK_CONFIG_NULL_OK},
  82.     {TK_CONFIG_BORDER, "-cursorbackground", "cursorBackground", "Foreground",
  83.     DEF_CANVAS_CURSOR_BG, Tk_Offset(Tk_Canvas, cursorBorder), 0},
  84.     {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
  85.     DEF_CANVAS_CURSOR_BD_COLOR, Tk_Offset(Tk_Canvas, cursorBorderWidth),
  86.     TK_CONFIG_COLOR_ONLY},
  87.     {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
  88.     DEF_CANVAS_CURSOR_BD_MONO, Tk_Offset(Tk_Canvas, cursorBorderWidth),
  89.     TK_CONFIG_MONO_ONLY},
  90.     {TK_CONFIG_INT, "-cursorofftime", "cursorOffTime", "OffTime",
  91.     DEF_CANVAS_CURSOR_OFF_TIME, Tk_Offset(Tk_Canvas, cursorOffTime), 0},
  92.     {TK_CONFIG_INT, "-cursorontime", "cursorOnTime", "OnTime",
  93.     DEF_CANVAS_CURSOR_ON_TIME, Tk_Offset(Tk_Canvas, cursorOnTime), 0},
  94.     {TK_CONFIG_PIXELS, "-cursorwidth", "cursorWidth", "CursorWidth",
  95.     DEF_CANVAS_CURSOR_WIDTH, Tk_Offset(Tk_Canvas, cursorWidth), 0},
  96.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  97.     DEF_CANVAS_HEIGHT, Tk_Offset(Tk_Canvas, height), 0},
  98.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  99.     DEF_CANVAS_RELIEF, Tk_Offset(Tk_Canvas, relief), 0},
  100.     {TK_CONFIG_PIXELS, "-scrollincrement", "scrollIncrement", "ScrollIncrement",
  101.     DEF_CANVAS_SCROLL_INCREMENT, Tk_Offset(Tk_Canvas, scrollIncrement), 0},
  102.     {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
  103.     DEF_CANVAS_SCROLL_REGION, Tk_Offset(Tk_Canvas, regionString), 0},
  104.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  105.     DEF_CANVAS_SELECT_COLOR, Tk_Offset(Tk_Canvas, selBorder),
  106.     TK_CONFIG_COLOR_ONLY},
  107.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  108.     DEF_CANVAS_SELECT_MONO, Tk_Offset(Tk_Canvas, selBorder),
  109.     TK_CONFIG_MONO_ONLY},
  110.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  111.     DEF_CANVAS_SELECT_BD_COLOR, Tk_Offset(Tk_Canvas, selBorderWidth),
  112.     TK_CONFIG_COLOR_ONLY},
  113.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  114.     DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(Tk_Canvas, selBorderWidth),
  115.     TK_CONFIG_MONO_ONLY},
  116.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  117.     DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(Tk_Canvas, selFgColorPtr),
  118.     TK_CONFIG_COLOR_ONLY},
  119.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  120.     DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(Tk_Canvas, selFgColorPtr),
  121.     TK_CONFIG_MONO_ONLY},
  122.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  123.     DEF_CANVAS_WIDTH, Tk_Offset(Tk_Canvas, width), 0},
  124.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  125.     DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(Tk_Canvas, xScrollCmd), 0},
  126.     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
  127.     DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(Tk_Canvas, yScrollCmd), 0},
  128.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  129.     (char *) NULL, 0, 0}
  130. };
  131.  
  132. /*
  133.  * List of all the item types known at present:
  134.  */
  135.  
  136. static Tk_ItemType *typeList = NULL;    /* NULL means initialization hasn't
  137.                      * been done yet. */
  138.  
  139. /*
  140.  * Standard item types provided by Tk:
  141.  */
  142.  
  143. extern Tk_ItemType TkArcType, TkBitmapType, TkLineType;
  144. extern Tk_ItemType TkOvalType, TkPolygonType;
  145. extern Tk_ItemType TkRectangleType, TkTextType, TkWindowType;
  146.  
  147. /*
  148.  * Various Tk_Uid's used by this module (set up during initialization):
  149.  */
  150.  
  151. static Tk_Uid allUid = NULL;
  152. static Tk_Uid currentUid = NULL;
  153.  
  154. /*
  155.  * Statistics counters:
  156.  */
  157.  
  158. static int numIdSearches;
  159. static int numSlowSearches;
  160.  
  161. /*
  162.  * Prototypes for procedures defined later in this file:
  163.  */
  164.  
  165. static void        CanvasBindProc _ANSI_ARGS_((ClientData clientData,
  166.                 XEvent *eventPtr));
  167. static void        CanvasBlinkProc _ANSI_ARGS_((ClientData clientData));
  168. static void        CanvasDoEvent _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  169.                 XEvent *eventPtr));
  170. static void        CanvasEventProc _ANSI_ARGS_((ClientData clientData,
  171.                 XEvent *eventPtr));
  172. static int        CanvasFetchSelection _ANSI_ARGS_((
  173.                 ClientData clientData, int offset,
  174.                 char *buffer, int maxBytes));
  175. static void        CanvasFocusProc _ANSI_ARGS_((ClientData clientData,
  176.                 int gotFocus));
  177. static void        CanvasLostSelection _ANSI_ARGS_((
  178.                 ClientData clientData));
  179. static void        CanvasSelectTo _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  180.                 Tk_Item *itemPtr, int index));
  181. static void        CanvasSetOrigin _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  182.                 int xOrigin, int yOrigin));
  183. static int        CanvasTagsParseProc _ANSI_ARGS_((ClientData clientData,
  184.                 Tcl_Interp *interp, Tk_Window tkwin, char *value,
  185.                 char *widgRec, int offset));
  186. static char *        CanvasTagsPrintProc _ANSI_ARGS_((ClientData clientData,
  187.                 Tk_Window tkwin, char *widgRec, int offset,
  188.                 Tcl_FreeProc **freeProcPtr));
  189. static void        CanvasUpdateScrollbars _ANSI_ARGS_((
  190.                 Tk_Canvas *canvasPtr));
  191. static int        CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData,
  192.                 Tcl_Interp *interp, int argc, char **argv));
  193. static int        ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
  194.                 Tk_Canvas *canvasPtr, int argc, char **argv,
  195.                 int flags));
  196. static void        DestroyCanvas _ANSI_ARGS_((ClientData clientData));
  197. static void        DisplayCanvas _ANSI_ARGS_((ClientData clientData));
  198. static void        DoItem _ANSI_ARGS_((Tcl_Interp *interp,
  199.                 Tk_Item *itemPtr, Tk_Uid tag));
  200. static void        EventuallyRedrawArea _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  201.                 int x1, int y1, int x2, int y2));
  202. static int        FindItems _ANSI_ARGS_((Tcl_Interp *interp,
  203.                 Tk_Canvas *canvasPtr, int argc, char **argv,
  204.                 char *newTag, char *cmdName, char *option));
  205. static int        FindArea _ANSI_ARGS_((Tcl_Interp *interp,
  206.                 Tk_Canvas *canvasPtr, char **argv, Tk_Uid uid,
  207.                 int enclosed));
  208. static double        GridAlign _ANSI_ARGS_((double coord, double spacing));
  209. static void        InitCanvas _ANSI_ARGS_((void));
  210. static Tk_Item *    NextItem _ANSI_ARGS_((TagSearch *searchPtr));
  211. static void        PickCurrentItem _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  212.                 XEvent *eventPtr));
  213. static void        RelinkItems _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  214.                 char *tag, Tk_Item *prevPtr));
  215. static Tk_Item *    StartTagSearch _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  216.                 char *tag, TagSearch *searchPtr));
  217.  
  218. /*
  219.  * Custom option for handling "-tags" options for canvas items:
  220.  */
  221.  
  222. Tk_CustomOption tkCanvasTagsOption = {
  223.     CanvasTagsParseProc,
  224.     CanvasTagsPrintProc,
  225.     (ClientData) NULL
  226. };
  227.  
  228. /*
  229.  *--------------------------------------------------------------
  230.  *
  231.  * Tk_CanvasCmd --
  232.  *
  233.  *    This procedure is invoked to process the "canvas" Tcl
  234.  *    command.  See the user documentation for details on what
  235.  *    it does.
  236.  *
  237.  * Results:
  238.  *    A standard Tcl result.
  239.  *
  240.  * Side effects:
  241.  *    See the user documentation.
  242.  *
  243.  *--------------------------------------------------------------
  244.  */
  245.  
  246. int
  247. Tk_CanvasCmd(clientData, interp, argc, argv)
  248.     ClientData clientData;        /* Main window associated with
  249.                  * interpreter. */
  250.     Tcl_Interp *interp;        /* Current interpreter. */
  251.     int argc;            /* Number of arguments. */
  252.     char **argv;        /* Argument strings. */
  253. {
  254.     Tk_Window tkwin = (Tk_Window) clientData;
  255.     register Tk_Canvas *canvasPtr;
  256.     Tk_Window new;
  257.  
  258.     if (typeList == NULL) {
  259.     InitCanvas();
  260.     }
  261.  
  262.     if (argc < 2) {
  263.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  264.         argv[0], " pathName ?options?\"", (char *) NULL);
  265.     return TCL_ERROR;
  266.     }
  267.  
  268.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  269.     if (new == NULL) {
  270.     return TCL_ERROR;
  271.     }
  272.  
  273.     /*
  274.      * Initialize fields that won't be initialized by ConfigureCanvas,
  275.      * or which ConfigureCanvas expects to have reasonable values
  276.      * (e.g. resource pointers).
  277.      */
  278.  
  279.     canvasPtr = (Tk_Canvas *) ckalloc(sizeof(Tk_Canvas));
  280.     canvasPtr->tkwin = new;
  281.     canvasPtr->interp = interp;
  282.     canvasPtr->firstItemPtr = NULL;
  283.     canvasPtr->lastItemPtr = NULL;
  284.     canvasPtr->bgBorder = NULL;
  285.     canvasPtr->bgColor = NULL;
  286.     canvasPtr->pixmapGC = None;
  287.     canvasPtr->selBorder = NULL;
  288.     canvasPtr->selFgColorPtr = NULL;
  289.     canvasPtr->selItemPtr = NULL;
  290.     canvasPtr->selectFirst = -1;
  291.     canvasPtr->selectLast = -1;
  292.     canvasPtr->cursorBorder = NULL;
  293.     canvasPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
  294.     canvasPtr->focusItemPtr = NULL;
  295.     canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
  296.     canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
  297.     canvasPtr->bindingTable = NULL;
  298.     canvasPtr->currentItemPtr = NULL;
  299.     canvasPtr->pickEvent.type = LeaveNotify;
  300.     canvasPtr->xScrollCmd = NULL;
  301.     canvasPtr->yScrollCmd = NULL;
  302.     canvasPtr->regionString = NULL;
  303.     canvasPtr->hotPtr = NULL;
  304.     canvasPtr->cursor = None;
  305.     canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(tkwin));
  306.     canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(tkwin));
  307.     canvasPtr->flags = 0;
  308.     canvasPtr->nextId = 1;
  309.  
  310.     Tk_SetClass(canvasPtr->tkwin, "Canvas");
  311.     Tk_CreateEventHandler(canvasPtr->tkwin, ExposureMask|StructureNotifyMask,
  312.         CanvasEventProc, (ClientData) canvasPtr);
  313.     Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
  314.         |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
  315.         |LeaveWindowMask|PointerMotionMask, CanvasBindProc,
  316.         (ClientData) canvasPtr);
  317.     Tk_CreateSelHandler(canvasPtr->tkwin, XA_STRING, CanvasFetchSelection,
  318.         (ClientData) canvasPtr, XA_STRING);
  319.     Tcl_CreateCommand(interp, Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd,
  320.         (ClientData) canvasPtr, (void (*)()) NULL);
  321.     if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) {
  322.     goto error;
  323.     }
  324.     Tk_CreateFocusHandler(canvasPtr->tkwin, CanvasFocusProc,
  325.         (ClientData) canvasPtr);
  326.  
  327.     interp->result = Tk_PathName(canvasPtr->tkwin);
  328.     return TCL_OK;
  329.  
  330.     error:
  331.     Tk_DestroyWindow(canvasPtr->tkwin);
  332.     return TCL_ERROR;
  333. }
  334.  
  335. /*
  336.  *--------------------------------------------------------------
  337.  *
  338.  * CanvasWidgetCmd --
  339.  *
  340.  *    This procedure is invoked to process the Tcl command
  341.  *    that corresponds to a widget managed by this module.
  342.  *    See the user documentation for details on what it does.
  343.  *
  344.  * Results:
  345.  *    A standard Tcl result.
  346.  *
  347.  * Side effects:
  348.  *    See the user documentation.
  349.  *
  350.  *--------------------------------------------------------------
  351.  */
  352.  
  353. static int
  354. CanvasWidgetCmd(clientData, interp, argc, argv)
  355.     ClientData clientData;        /* Information about canvas
  356.                      * widget. */
  357.     Tcl_Interp *interp;            /* Current interpreter. */
  358.     int argc;                /* Number of arguments. */
  359.     char **argv;            /* Argument strings. */
  360. {
  361.     register Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  362.     int length, result;
  363.     char c;
  364.     Tk_Item *itemPtr = NULL;        /* Initialization needed only to
  365.                      * prevent compiler warning. */
  366.     TagSearch search;
  367.  
  368.     if (argc < 2) {
  369.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  370.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  371.     return TCL_ERROR;
  372.     }
  373.     Tk_Preserve((ClientData) canvasPtr);
  374.     result = TCL_OK;
  375.     c = argv[1][0];
  376.     length = strlen(argv[1]);
  377.     if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
  378.     if (argc < 4) {
  379.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  380.             argv[0], " addtags tag searchCommand ?arg arg ...?\"",
  381.             (char *) NULL);
  382.         goto error;
  383.     }
  384.     result = FindItems(interp, canvasPtr, argc-3, argv+3, argv[2], argv[0],
  385.         " addtag tag");
  386.     } else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)
  387.         && (length >= 2)) {
  388.     int i, gotAny;
  389.     int x1 = 0, y1 = 0, x2 = 0, y2 = 0;    /* Initializations needed
  390.                          * only to prevent compiler
  391.                          * warnings. */
  392.  
  393.     if (argc < 3) {
  394.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  395.             argv[0], " bbox tagOrId ?tagOrId ...?\"",
  396.             (char *) NULL);
  397.         goto error;
  398.     }
  399.     gotAny = 0;
  400.     for (i = 2; i < argc; i++) {
  401.         for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
  402.             itemPtr != NULL; itemPtr = NextItem(&search)) {
  403.         if (!gotAny) {
  404.             x1 = itemPtr->x1;
  405.             y1 = itemPtr->y1;
  406.             x2 = itemPtr->x2;
  407.             y2 = itemPtr->y2;
  408.             gotAny = 1;
  409.         } else {
  410.             if (itemPtr->x1 < x1) {
  411.             x1 = itemPtr->x1;
  412.             }
  413.             if (itemPtr->y1 < y1) {
  414.             y1 = itemPtr->y1;
  415.             }
  416.             if (itemPtr->x2 > x2) {
  417.             x2 = itemPtr->x2;
  418.             }
  419.             if (itemPtr->y2 > y2) {
  420.             y2 = itemPtr->y2;
  421.             }
  422.         }
  423.         }
  424.     }
  425.     if (gotAny) {
  426.         sprintf(interp->result, "%d %d %d %d", x1, y1, x2, y2);
  427.     }
  428.     } else if ((c == 'b') && (strncmp(argv[1], "bind", length) == 0)
  429.         && (length >= 2)) {
  430.     ClientData object;
  431.  
  432.     if ((argc < 3) || (argc > 5)) {
  433.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  434.             argv[0], " bind tagOrId ?sequence? ?command?\"",
  435.             (char *) NULL);
  436.         goto error;
  437.     }
  438.  
  439.     /*
  440.      * Figure out what object to use for the binding (individual
  441.      * item vs. tag).
  442.      */
  443.  
  444.     object = 0;
  445.     if (isdigit(argv[2][0])) {
  446.         int id;
  447.         char *end;
  448.  
  449.         id = strtoul(argv[2], &end, 0);
  450.         if (*end != 0) {
  451.         goto bindByTag;
  452.         }
  453.         for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  454.             itemPtr = itemPtr->nextPtr) {
  455.         if (itemPtr->id == id) {
  456.             object = (ClientData) itemPtr;
  457.             break;
  458.         }
  459.         }
  460.         if (object == 0) {
  461.         Tcl_AppendResult(interp, "item \"", argv[2],
  462.             "\" doesn't exist", (char *) NULL);
  463.         goto error;
  464.         }
  465.     } else {
  466.         bindByTag:
  467.         object = (ClientData) Tk_GetUid(argv[2]);
  468.     }
  469.  
  470.     /*
  471.      * Make a binding table if the canvas doesn't already have
  472.      * one.
  473.      */
  474.  
  475.     if (canvasPtr->bindingTable == NULL) {
  476.         canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
  477.     }
  478.  
  479.     if (argc == 5) {
  480.         int append = 0;
  481.         unsigned long mask;
  482.  
  483.         if (argv[4][0] == 0) {
  484.         result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
  485.             object, argv[3]);
  486.         goto done;
  487.         }
  488.         if (argv[4][0] == '+') {
  489.         argv[4]++;
  490.         append = 1;
  491.         }
  492.         mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
  493.             object, argv[3], argv[4], append);
  494.         if (mask == 0) {
  495.         goto error;
  496.         }
  497.         if (mask & ~(ButtonMotionMask|Button1MotionMask|Button2MotionMask
  498.             |Button3MotionMask|Button4MotionMask|Button5MotionMask
  499.             |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
  500.             |LeaveWindowMask|KeyPressMask|KeyReleaseMask
  501.             |PointerMotionMask)) {
  502.         Tk_DeleteBinding(interp, canvasPtr->bindingTable,
  503.             object, argv[3]);
  504.         Tcl_ResetResult(interp);
  505.         Tcl_AppendResult(interp, "requested illegal events; ",
  506.             "only key, button, motion, and enter/leave ",
  507.             "events may be used", (char *) NULL);
  508.         goto error;
  509.         }
  510.     } else if (argc == 4) {
  511.         char *command;
  512.     
  513.         command = Tk_GetBinding(interp, canvasPtr->bindingTable,
  514.             object, argv[3]);
  515.         if (command == NULL) {
  516.         goto error;
  517.         }
  518.         interp->result = command;
  519.     } else {
  520.         Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
  521.     }
  522.     } else if ((c == 'c') && (strcmp(argv[1], "canvasx") == 0)) {
  523.     int x;
  524.     double grid;
  525.  
  526.     if ((argc < 3) || (argc > 4)) {
  527.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  528.             argv[0], " canvasx screenx ?gridspacing?\"",
  529.             (char *) NULL);
  530.         goto error;
  531.     }
  532.     if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
  533.         goto error;
  534.     }
  535.     if (argc == 4) {
  536.         if (TkGetCanvasCoord(canvasPtr, argv[3], &grid) != TCL_OK) {
  537.         goto error;
  538.         }
  539.     } else {
  540.         grid = 0.0;
  541.     }
  542.     x += canvasPtr->xOrigin;
  543.     sprintf(interp->result, "%g", GridAlign((double) x, grid));
  544.     } else if ((c == 'c') && (strcmp(argv[1], "canvasy") == 0)) {
  545.     int y;
  546.     double grid;
  547.  
  548.     if ((argc < 3) || (argc > 4)) {
  549.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  550.             argv[0], " canvasy screeny ?gridspacing?\"",
  551.             (char *) NULL);
  552.         goto error;
  553.     }
  554.     if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
  555.         goto error;
  556.     }
  557.     if (argc == 4) {
  558.         if (TkGetCanvasCoord(canvasPtr, argv[3], &grid) != TCL_OK) {
  559.         goto error;
  560.         }
  561.     } else {
  562.         grid = 0.0;
  563.     }
  564.     y += canvasPtr->yOrigin;
  565.     sprintf(interp->result, "%g", GridAlign((double) y, grid));
  566.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  567.         && (length >= 3)) {
  568.     if (argc == 2) {
  569.         result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
  570.             (char *) canvasPtr, (char *) NULL, 0);
  571.     } else if (argc == 3) {
  572.         result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
  573.             (char *) canvasPtr, argv[2], 0);
  574.     } else {
  575.         result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
  576.             TK_CONFIG_ARGV_ONLY);
  577.     }
  578.     } else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
  579.         && (length >= 3)) {
  580.     if (argc < 3) {
  581.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  582.             argv[0], " coords tagOrId ?x y x y ...?\"",
  583.             (char *) NULL);
  584.         goto error;
  585.     }
  586.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  587.     if (itemPtr != NULL) {
  588.         if (argc != 3) {
  589.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  590.             itemPtr->x2, itemPtr->y2);
  591.         }
  592.         if (itemPtr->typePtr->coordProc != NULL) {
  593.         result = (*itemPtr->typePtr->coordProc)(canvasPtr, itemPtr,
  594.             argc-3, argv+3);
  595.         }
  596.         if (argc != 3) {
  597.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  598.             itemPtr->x2, itemPtr->y2);
  599.         }
  600.     }
  601.     } else if ((c == 'c') && (strncmp(argv[1], "create", length) == 0)
  602.         && (length >= 2)) {
  603.     register Tk_ItemType *typePtr;
  604.     Tk_ItemType *matchPtr = NULL;
  605.     register Tk_Item *itemPtr;
  606.  
  607.     if (argc < 3) {
  608.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  609.             argv[0], " create type ?arg arg ...?\"", (char *) NULL);
  610.         goto error;
  611.     }
  612.     c = argv[2][0];
  613.     length = strlen(argv[2]);
  614.     for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
  615.         if ((c == typePtr->name[0])
  616.             && (strncmp(argv[2], typePtr->name, length) == 0)) {
  617.         if (matchPtr != NULL) {
  618.             badType:
  619.             Tcl_AppendResult(interp,
  620.                 "unknown or ambiguous item type \"",
  621.                 argv[2], "\"", (char *) NULL);
  622.             goto error;
  623.         }
  624.         matchPtr = typePtr;
  625.         }
  626.     }
  627.     if (matchPtr == NULL) {
  628.         goto badType;
  629.     }
  630.     typePtr = matchPtr;
  631.     itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
  632.     itemPtr->id = canvasPtr->nextId;
  633.     canvasPtr->nextId++;
  634.     itemPtr->tagPtr = itemPtr->staticTagSpace;
  635.     itemPtr->tagSpace = TK_TAG_SPACE;
  636.     itemPtr->numTags = 0;
  637.     itemPtr->typePtr = typePtr;
  638.     if ((*typePtr->createProc)(canvasPtr, itemPtr, argc-3, argv+3)
  639.         != TCL_OK) {
  640.         ckfree((char *) itemPtr);
  641.         goto error;
  642.     }
  643.     itemPtr->nextPtr = NULL;
  644.     canvasPtr->hotPtr = itemPtr;
  645.     canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
  646.     if (canvasPtr->lastItemPtr == NULL) {
  647.         canvasPtr->firstItemPtr = itemPtr;
  648.     } else {
  649.         canvasPtr->lastItemPtr->nextPtr = itemPtr;
  650.     }
  651.     canvasPtr->lastItemPtr = itemPtr;
  652.     EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  653.         itemPtr->x2, itemPtr->y2);
  654.     canvasPtr->flags |= REPICK_NEEDED;
  655.     sprintf(interp->result, "%d", itemPtr->id);
  656.     } else if ((c == 'c') && (strncmp(argv[1], "cursor", length) == 0)
  657.         && (length >= 2)) {
  658.     int index;
  659.  
  660.     if (argc != 4) {
  661.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  662.             argv[0], " cursor tagOrId index\"",
  663.             (char *) NULL);
  664.         goto error;
  665.     }
  666.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  667.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  668.         if ((itemPtr->typePtr->indexProc == NULL)
  669.             || (itemPtr->typePtr->cursorProc == NULL)) {
  670.         goto done;
  671.         }
  672.         if ((*itemPtr->typePtr->indexProc)(canvasPtr, itemPtr,
  673.             argv[3], &index) != TCL_OK) {
  674.         goto error;
  675.         }
  676.         (*itemPtr->typePtr->cursorProc)(canvasPtr, itemPtr, index);
  677.         if ((itemPtr == canvasPtr->focusItemPtr)
  678.             && (canvasPtr->flags & CURSOR_ON)) {
  679.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  680.             itemPtr->x2, itemPtr->y2);
  681.         }
  682.     }
  683.     } else if ((c == 'd') && (strncmp(argv[1], "dchars", length) == 0)
  684.         && (length >= 2)) {
  685.     int first, last;
  686.  
  687.     if ((argc != 4) && (argc != 5)) {
  688.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  689.             argv[0], " dchars tagOrId first ?last?\"",
  690.             (char *) NULL);
  691.         goto error;
  692.     }
  693.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  694.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  695.         if ((itemPtr->typePtr->indexProc == NULL)
  696.             || (itemPtr->typePtr->dCharsProc == NULL)) {
  697.         continue;
  698.         }
  699.         if ((*itemPtr->typePtr->indexProc)(canvasPtr, itemPtr,
  700.             argv[3], &first) != TCL_OK) {
  701.         goto error;
  702.         }
  703.         if (argc == 5) {
  704.         if ((*itemPtr->typePtr->indexProc)(canvasPtr, itemPtr,
  705.             argv[4], &last) != TCL_OK) {
  706.             goto error;
  707.         }
  708.         } else {
  709.         last = first;
  710.         }
  711.  
  712.         /*
  713.          * Redraw both item's old and new areas:  it's possible
  714.          * that a delete could result in a new area larger than
  715.          * the old area.
  716.          */
  717.  
  718.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  719.             itemPtr->x2, itemPtr->y2);
  720.         result = (*itemPtr->typePtr->dCharsProc)(canvasPtr, itemPtr,
  721.             first, last);
  722.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  723.             itemPtr->x2, itemPtr->y2);
  724.         if (result != TCL_OK) {
  725.         goto error;
  726.         }
  727.     }
  728.     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
  729.         && (length >= 2)) {
  730.     if (argc != 3) {
  731.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  732.             argv[0], " delete tagOrId\"",
  733.             (char *) NULL);
  734.         goto error;
  735.     }
  736.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  737.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  738.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  739.         itemPtr->x2, itemPtr->y2);
  740.         (*itemPtr->typePtr->deleteProc)(itemPtr);
  741.         if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  742.         ckfree((char *) itemPtr->tagPtr);
  743.         }
  744.         if (search.prevPtr == NULL) {
  745.         canvasPtr->firstItemPtr = itemPtr->nextPtr;
  746.         if (canvasPtr->firstItemPtr == NULL) {
  747.             canvasPtr->lastItemPtr = NULL;
  748.         }
  749.         } else {
  750.         search.prevPtr->nextPtr = itemPtr->nextPtr;
  751.         }
  752.         if (canvasPtr->lastItemPtr == itemPtr) {
  753.         canvasPtr->lastItemPtr = search.prevPtr;
  754.         }
  755.         ckfree((char *) itemPtr);
  756.         if (itemPtr == canvasPtr->currentItemPtr) {
  757.         canvasPtr->currentItemPtr = NULL;
  758.         canvasPtr->flags |= REPICK_NEEDED;
  759.         }
  760.         if (itemPtr == canvasPtr->focusItemPtr) {
  761.         canvasPtr->focusItemPtr = NULL;
  762.         }
  763.         if (itemPtr == canvasPtr->selItemPtr) {
  764.         canvasPtr->selItemPtr = NULL;
  765.         }
  766.         if ((itemPtr == canvasPtr->hotPtr)
  767.             || (itemPtr = canvasPtr->hotPrevPtr)) {
  768.         canvasPtr->hotPtr = NULL;
  769.         }
  770.     }
  771.     } else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
  772.         && (length >= 2)) {
  773.     Tk_Uid tag;
  774.     int i;
  775.  
  776.     if ((argc != 3) && (argc != 4)) {
  777.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  778.             argv[0], " dtag tagOrId ?tagToDelete?\"",
  779.             (char *) NULL);
  780.         goto error;
  781.     }
  782.     if (argc == 4) {
  783.         tag = Tk_GetUid(argv[3]);
  784.     } else {
  785.         tag = Tk_GetUid(argv[2]);
  786.     }
  787.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  788.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  789.         for (i = itemPtr->numTags-1; i >= 0; i--) {
  790.         if (itemPtr->tagPtr[i] == tag) {
  791.             itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
  792.             itemPtr->numTags--;
  793.         }
  794.         }
  795.     }
  796.     } else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
  797.         && (length >= 2)) {
  798.     if (argc < 3) {
  799.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  800.             argv[0], " find searchCommand ?arg arg ...?\"",
  801.             (char *) NULL);
  802.         goto error;
  803.     }
  804.     result = FindItems(interp, canvasPtr, argc-2, argv+2, (char *) NULL,
  805.         argv[0]," find");
  806.     } else if ((c == 'f') && (strncmp(argv[1], "focus", length) == 0)
  807.         && (length >= 2)) {
  808.     if (argc > 3) {
  809.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  810.             argv[0], " focus ?tagOrId?\"",
  811.             (char *) NULL);
  812.         goto error;
  813.     }
  814.     itemPtr = canvasPtr->focusItemPtr;
  815.     if (argc == 2) {
  816.         if (itemPtr != NULL) {
  817.         sprintf(interp->result, "%d", itemPtr->id);
  818.         }
  819.         goto done;
  820.     }
  821.     if ((itemPtr != NULL) && (canvasPtr->flags & GOT_FOCUS)) {
  822.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  823.             itemPtr->x2, itemPtr->y2);
  824.     }
  825.     if (argv[2][0] == 0) {
  826.         canvasPtr->focusItemPtr = NULL;
  827.         goto done;
  828.     }
  829.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  830.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  831.         if (itemPtr->typePtr->cursorProc != NULL) {
  832.         break;
  833.         }
  834.     }
  835.     if (itemPtr == NULL) {
  836.         goto done;
  837.     }
  838.     canvasPtr->focusItemPtr = itemPtr;
  839.     if (canvasPtr->flags & GOT_FOCUS) {
  840.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  841.             itemPtr->x2, itemPtr->y2);
  842.     }
  843.     } else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
  844.     if (argc != 3) {
  845.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  846.             argv[0], " gettags tagOrId\"", (char *) NULL);
  847.         goto error;
  848.     }
  849.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  850.     if (itemPtr != NULL) {
  851.         int i;
  852.         for (i = 0; i < itemPtr->numTags; i++) {
  853.         Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i], 0);
  854.         }
  855.     }
  856.     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
  857.         && (length >= 3)) {
  858.     int index;
  859.  
  860.     if (argc != 4) {
  861.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  862.             argv[0], " index tagOrId string\"",
  863.             (char *) NULL);
  864.         goto error;
  865.     }
  866.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  867.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  868.         if (itemPtr->typePtr->indexProc != NULL) {
  869.         break;
  870.         }
  871.     }
  872.     if (itemPtr == NULL) {
  873.         Tcl_AppendResult(interp, "can't find an indexable item \"",
  874.             argv[2], "\"", (char *) NULL);
  875.         goto error;
  876.     }
  877.     if ((*itemPtr->typePtr->indexProc)(canvasPtr, itemPtr,
  878.         argv[3], &index) != TCL_OK) {
  879.         goto error;
  880.     }
  881.     sprintf(interp->result, "%d", index);
  882.     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
  883.         && (length >= 3)) {
  884.     int beforeThis;
  885.  
  886.     if (argc != 5) {
  887.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  888.             argv[0], " insert tagOrId beforeThis string\"",
  889.             (char *) NULL);
  890.         goto error;
  891.     }
  892.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  893.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  894.         if ((itemPtr->typePtr->indexProc == NULL)
  895.             || (itemPtr->typePtr->insertProc == NULL)) {
  896.         continue;
  897.         }
  898.         if ((*itemPtr->typePtr->indexProc)(canvasPtr, itemPtr,
  899.             argv[3], &beforeThis) != TCL_OK) {
  900.         goto error;
  901.         }
  902.  
  903.         /*
  904.          * Redraw both item's old and new areas:  it's possible
  905.          * that an insertion could result in a new area either
  906.          * larger or smaller than the old area.
  907.          */
  908.  
  909.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  910.             itemPtr->x2, itemPtr->y2);
  911.         result = (*itemPtr->typePtr->insertProc)(canvasPtr, itemPtr,
  912.             beforeThis, argv[4]);
  913.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  914.             itemPtr->x2, itemPtr->y2);
  915.         if (result != TCL_OK) {
  916.         goto error;
  917.         }
  918.     }
  919.     } else if ((c == 'i') && (strncmp(argv[1], "itemconfigure", length) == 0)
  920.         && (length >= 2)) {
  921.     if (argc < 3) {
  922.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  923.             argv[0], " itemconfigure tagOrId ?option value ...?\"",
  924.             (char *) NULL);
  925.         goto error;
  926.     }
  927.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  928.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  929.         if (argc == 3) {
  930.         result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
  931.             itemPtr->typePtr->configSpecs, (char *) itemPtr,
  932.             (char *) NULL, 0);
  933.         } else if (argc == 4) {
  934.         result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
  935.             itemPtr->typePtr->configSpecs, (char *) itemPtr,
  936.             argv[3], 0);
  937.         } else {
  938.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  939.             itemPtr->x2, itemPtr->y2);
  940.         result = (*itemPtr->typePtr->configProc)(canvasPtr, itemPtr,
  941.             argc-3, argv+3, TK_CONFIG_ARGV_ONLY);
  942.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  943.             itemPtr->x2, itemPtr->y2);
  944.         canvasPtr->flags |= REPICK_NEEDED;
  945.         }
  946.         if ((result != TCL_OK) || (argc < 5)) {
  947.         break;
  948.         }
  949.     }
  950.     } else if ((c == 'l') && (strncmp(argv[1], "lower", length) == 0)) {
  951.     Tk_Item *prevPtr;
  952.  
  953.     if ((argc != 3) && (argc != 4)) {
  954.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  955.             argv[0], " lower tagOrId ?belowThis?\"",
  956.             (char *) NULL);
  957.         goto error;
  958.     }
  959.  
  960.     /*
  961.      * First find the item just after which we'll insert the
  962.      * named items.
  963.      */
  964.  
  965.     if (argc == 3) {
  966.         prevPtr = NULL;
  967.     } else {
  968.         prevPtr = StartTagSearch(canvasPtr, argv[3], &search);
  969.         if (prevPtr != NULL) {
  970.         prevPtr = search.prevPtr;
  971.         } else {
  972.         Tcl_AppendResult(interp, "tag \"", argv[3],
  973.             "\" doesn't match any items", (char *) NULL);
  974.         goto error;
  975.         }
  976.     }
  977.     RelinkItems(canvasPtr, argv[2], prevPtr);
  978.     } else if ((c == 'm') && (strncmp(argv[1], "move", length) == 0)) {
  979.     double xAmount, yAmount;
  980.  
  981.     if (argc != 5) {
  982.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  983.             argv[0], " move tagOrId xAmount yAmount\"",
  984.             (char *) NULL);
  985.         goto error;
  986.     }
  987.     if ((TkGetCanvasCoord(canvasPtr, argv[3], &xAmount) != TCL_OK)
  988.         || (TkGetCanvasCoord(canvasPtr, argv[4], &yAmount) != TCL_OK)) {
  989.         goto error;
  990.     }
  991.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  992.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  993.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  994.         itemPtr->x2, itemPtr->y2);
  995.         (void) (*itemPtr->typePtr->translateProc)(canvasPtr, itemPtr,
  996.             xAmount, yAmount);
  997.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  998.         itemPtr->x2, itemPtr->y2);
  999.         canvasPtr->flags |= REPICK_NEEDED;
  1000.     }
  1001.     } else if ((c == 'r') && (strncmp(argv[1], "raise", length) == 0)) {
  1002.     Tk_Item *prevPtr;
  1003.  
  1004.     if ((argc != 3) && (argc != 4)) {
  1005.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1006.             argv[0], " raise tagOrId ?aboveThis?\"",
  1007.             (char *) NULL);
  1008.         goto error;
  1009.     }
  1010.  
  1011.     /*
  1012.      * First find the item just after which we'll insert the
  1013.      * named items.
  1014.      */
  1015.  
  1016.     if (argc == 3) {
  1017.         prevPtr = canvasPtr->lastItemPtr;
  1018.     } else {
  1019.         prevPtr = NULL;
  1020.         for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
  1021.             itemPtr != NULL; itemPtr = NextItem(&search)) {
  1022.         prevPtr = itemPtr;
  1023.         }
  1024.         if (prevPtr == NULL) {
  1025.         Tcl_AppendResult(interp, "tagOrId \"", argv[3],
  1026.             "\" doesn't match any items", (char *) NULL);
  1027.         goto error;
  1028.         }
  1029.     }
  1030.     RelinkItems(canvasPtr, argv[2], prevPtr);
  1031.     } else if ((c == 's') && (strncmp(argv[1], "scale", length) == 0)
  1032.         && (length >= 3)) {
  1033.     double xOrigin, yOrigin, xScale, yScale;
  1034.  
  1035.     if (argc != 7) {
  1036.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1037.             argv[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
  1038.             (char *) NULL);
  1039.         goto error;
  1040.     }
  1041.     if ((TkGetCanvasCoord(canvasPtr, argv[3], &xOrigin) != TCL_OK)
  1042.         || (TkGetCanvasCoord(canvasPtr, argv[4], &yOrigin) != TCL_OK)
  1043.         || (Tcl_GetDouble(interp, argv[5], &xScale) != TCL_OK)
  1044.         || (Tcl_GetDouble(interp, argv[6], &yScale) != TCL_OK)) {
  1045.         goto error;
  1046.     }
  1047.     if ((xScale <= 0.0) || (yScale <= 0.0)) {
  1048.         interp->result = "scale factors must be greater than zero";
  1049.         goto error;
  1050.     }
  1051.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  1052.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  1053.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  1054.         itemPtr->x2, itemPtr->y2);
  1055.         (void) (*itemPtr->typePtr->scaleProc)(canvasPtr, itemPtr,
  1056.             xOrigin, yOrigin, xScale, yScale);
  1057.         EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  1058.         itemPtr->x2, itemPtr->y2);
  1059.         canvasPtr->flags |= REPICK_NEEDED;
  1060.     }
  1061.     } else if ((c == 's') && (strncmp(argv[1], "scan", length) == 0)
  1062.         && (length >= 3)) {
  1063.     int x, y;
  1064.  
  1065.     if (argc != 5) {
  1066.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1067.             argv[0], " scan mark|dragto x y\"", (char *) NULL);
  1068.         goto error;
  1069.     }
  1070.     if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  1071.         || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
  1072.         goto error;
  1073.     }
  1074.     if ((argv[2][0] == 'm')
  1075.         && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  1076.         canvasPtr->scanX = x;
  1077.         canvasPtr->scanXOrigin = canvasPtr->xOrigin;
  1078.         canvasPtr->scanY = y;
  1079.         canvasPtr->scanYOrigin = canvasPtr->yOrigin;
  1080.     } else if ((argv[2][0] == 'd')
  1081.         && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  1082.         int newXOrigin, newYOrigin, tmp;
  1083.  
  1084.         /*
  1085.          * Compute a new view origin for the canvas, amplifying the
  1086.          * mouse motion and rounding to the nearest multiple of the
  1087.          * scroll increment.
  1088.          */
  1089.  
  1090.         tmp = canvasPtr->scanXOrigin - 10*(x - canvasPtr->scanX)
  1091.             - canvasPtr->scrollX1;
  1092.         if (tmp >= 0) {
  1093.         tmp = (tmp + canvasPtr->scrollIncrement/2)
  1094.             /canvasPtr->scrollIncrement;
  1095.         } else {
  1096.         tmp = -(((-tmp) + canvasPtr->scrollIncrement/2)
  1097.             /canvasPtr->scrollIncrement);
  1098.         }
  1099.         newXOrigin = canvasPtr->scrollX1 + tmp*canvasPtr->scrollIncrement;
  1100.         tmp = canvasPtr->scanYOrigin - 10*(y - canvasPtr->scanY)
  1101.             - canvasPtr->scrollY1;
  1102.         if (tmp >= 0) {
  1103.         tmp = (tmp + canvasPtr->scrollIncrement/2)
  1104.             /canvasPtr->scrollIncrement;
  1105.         } else {
  1106.         tmp = -(((-tmp) + canvasPtr->scrollIncrement/2)
  1107.             /canvasPtr->scrollIncrement);
  1108.         }
  1109.         newYOrigin = canvasPtr->scrollY1 + tmp*canvasPtr->scrollIncrement;
  1110.         CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
  1111.     } else {
  1112.         Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  1113.             "\":  must be mark or dragto", (char *) NULL);
  1114.         goto error;
  1115.     }
  1116.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  1117.         && (length >= 2)) {
  1118.     int index;
  1119.  
  1120.     if (argc < 3) {
  1121.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1122.             argv[0], " select option ?tagOrId? ?arg?\"", (char *) NULL);
  1123.         goto error;
  1124.     }
  1125.     if (argc >= 4) {
  1126.         for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
  1127.             itemPtr != NULL; itemPtr = NextItem(&search)) {
  1128.         if ((itemPtr->typePtr->indexProc != NULL)
  1129.             && (itemPtr->typePtr->selectionProc != NULL)){
  1130.             break;
  1131.         }
  1132.         }
  1133.         if (itemPtr == NULL) {
  1134.         Tcl_AppendResult(interp,
  1135.             "can't find an indexable and selectable item \"",
  1136.             argv[3], "\"", (char *) NULL);
  1137.         goto error;
  1138.         }
  1139.     }
  1140.     if (argc == 5) {
  1141.         if ((*itemPtr->typePtr->indexProc)(canvasPtr, itemPtr,
  1142.             argv[4], &index) != TCL_OK) {
  1143.         goto error;
  1144.         }
  1145.     }
  1146.     length = strlen(argv[2]);
  1147.     c = argv[2][0];
  1148.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  1149.         if (argc != 5) {
  1150.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1151.             argv[0], " select adjust tagOrId index\"",
  1152.             (char *) NULL);
  1153.         goto error;
  1154.         }
  1155.         if (canvasPtr->selItemPtr == itemPtr) {
  1156.         if (index < (canvasPtr->selectFirst
  1157.             + canvasPtr->selectLast)/2) {
  1158.             canvasPtr->selectAnchor = canvasPtr->selectLast + 1;
  1159.         } else {
  1160.             canvasPtr->selectAnchor = canvasPtr->selectFirst;
  1161.         }
  1162.         }
  1163.         CanvasSelectTo(canvasPtr, itemPtr, index);
  1164.     } else if ((c == 'c') && (argv[2] != NULL)
  1165.         && (strncmp(argv[2], "clear", length) == 0)) {
  1166.         if (argc != 3) {
  1167.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1168.             argv[0], " select clear\"", (char *) NULL);
  1169.         goto error;
  1170.         }
  1171.         if (canvasPtr->selItemPtr != NULL) {
  1172.         EventuallyRedrawArea(canvasPtr, canvasPtr->selItemPtr->x1,
  1173.             canvasPtr->selItemPtr->y1, canvasPtr->selItemPtr->x2,
  1174.             canvasPtr->selItemPtr->y2);
  1175.         canvasPtr->selItemPtr = NULL;
  1176.         }
  1177.         goto done;
  1178.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  1179.         if (argc != 5) {
  1180.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1181.             argv[0], " select from tagOrId index\"",
  1182.             (char *) NULL);
  1183.         goto error;
  1184.         }
  1185.         canvasPtr->anchorItemPtr = itemPtr;
  1186.         canvasPtr->selectAnchor = index;
  1187.     } else if ((c == 'i') && (strncmp(argv[2], "item", length) == 0)) {
  1188.         if (argc != 3) {
  1189.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1190.             argv[0], " select item\"", (char *) NULL);
  1191.         goto error;
  1192.         }
  1193.         if (canvasPtr->selItemPtr != NULL) {
  1194.         sprintf(interp->result, "%d", canvasPtr->selItemPtr->id);
  1195.         }
  1196.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  1197.         if (argc != 5) {
  1198.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1199.             argv[0], " select to tagOrId index\"",
  1200.             (char *) NULL);
  1201.         goto error;
  1202.         }
  1203.         CanvasSelectTo(canvasPtr, itemPtr, index);
  1204.     } else {
  1205.         Tcl_AppendResult(interp, "bad select option \"", argv[2],
  1206.             "\": must be adjust, clear, from, item, or to",
  1207.             (char *) NULL);
  1208.         goto error;
  1209.     }
  1210.     } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)) {
  1211.     if (argc != 3) {
  1212.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1213.             argv[0], " type tag\"", (char *) NULL);
  1214.         goto error;
  1215.     }
  1216.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  1217.     if (itemPtr != NULL) {
  1218.         interp->result = itemPtr->typePtr->name;
  1219.     }
  1220.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  1221.     int index;
  1222.  
  1223.     if (argc != 3) {
  1224.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1225.             argv[0], " xview index\"", (char *) NULL);
  1226.         goto error;
  1227.     }
  1228.     if (Tcl_GetInt(canvasPtr->interp, argv[2], &index) != TCL_OK) {
  1229.         goto error;
  1230.     }
  1231.     CanvasSetOrigin(canvasPtr,
  1232.         (canvasPtr->scrollX1 + index*canvasPtr->scrollIncrement),
  1233.         canvasPtr->yOrigin);
  1234.     } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
  1235.     int index;
  1236.  
  1237.     if (argc != 3) {
  1238.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1239.             argv[0], " yview index\"", (char *) NULL);
  1240.         goto error;
  1241.     }
  1242.     if (Tcl_GetInt(canvasPtr->interp, argv[2], &index) != TCL_OK) {
  1243.         goto error;
  1244.     }
  1245.     CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin,
  1246.         (canvasPtr->scrollY1 + index*canvasPtr->scrollIncrement));
  1247.     } else {
  1248.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  1249.         "\":  must be addtag, bbox, bind, ",
  1250.         "canvasx, canvasy, configure, coords, create, ",
  1251.         "cursor, dchars, delete, dtag, find, focus, ",
  1252.         "gettags, index, insert, itemconfigure, lower, ",
  1253.         "move, raise, scale, scan, select, type, xview, or yview",
  1254.         (char *) NULL);  
  1255.     goto error;
  1256.     }
  1257.     done:
  1258.     Tk_Release((ClientData) canvasPtr);
  1259.     return result;
  1260.  
  1261.     error:
  1262.     Tk_Release((ClientData) canvasPtr);
  1263.     return TCL_ERROR;
  1264. }
  1265.  
  1266. /*
  1267.  *----------------------------------------------------------------------
  1268.  *
  1269.  * DestroyCanvas --
  1270.  *
  1271.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1272.  *    to clean up the internal structure of a canvas at a safe time
  1273.  *    (when no-one is using it anymore).
  1274.  *
  1275.  * Results:
  1276.  *    None.
  1277.  *
  1278.  * Side effects:
  1279.  *    Everything associated with the canvas is freed up.
  1280.  *
  1281.  *----------------------------------------------------------------------
  1282.  */
  1283.  
  1284. static void
  1285. DestroyCanvas(clientData)
  1286.     ClientData clientData;    /* Info about canvas widget. */
  1287. {
  1288.     register Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  1289.     register Tk_Item *itemPtr;
  1290.  
  1291.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1292.         itemPtr = canvasPtr->firstItemPtr) {
  1293.     canvasPtr->firstItemPtr = itemPtr->nextPtr;
  1294.     (*itemPtr->typePtr->deleteProc)(itemPtr);
  1295.     if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  1296.         ckfree((char *) itemPtr->tagPtr);
  1297.     }
  1298.     ckfree((char *) itemPtr);
  1299.     }
  1300.  
  1301.     if (canvasPtr->bgBorder != NULL) {
  1302.     Tk_Free3DBorder(canvasPtr->bgBorder);
  1303.     }
  1304.     if (canvasPtr->bgColor != NULL) {
  1305.     Tk_FreeColor(canvasPtr->bgColor);
  1306.     }
  1307.     if (canvasPtr->pixmapGC != None) {
  1308.     Tk_FreeGC(canvasPtr->pixmapGC);
  1309.     }
  1310.     if (canvasPtr->selBorder != NULL) {
  1311.     Tk_Free3DBorder(canvasPtr->selBorder);
  1312.     }
  1313.     if (canvasPtr->selFgColorPtr != NULL) {
  1314.     Tk_FreeColor(canvasPtr->selFgColorPtr);
  1315.     }
  1316.     if (canvasPtr->cursorBorder != NULL) {
  1317.     Tk_Free3DBorder(canvasPtr->cursorBorder);
  1318.     }
  1319.     Tk_DeleteTimerHandler(canvasPtr->cursorBlinkHandler);
  1320.     if (canvasPtr->bindingTable != NULL) {
  1321.     Tk_DeleteBindingTable(canvasPtr->bindingTable);
  1322.     }
  1323.     if (canvasPtr->xScrollCmd != NULL) {
  1324.     ckfree(canvasPtr->xScrollCmd);
  1325.     }
  1326.     if (canvasPtr->yScrollCmd != NULL) {
  1327.     ckfree(canvasPtr->yScrollCmd);
  1328.     }
  1329.     if (canvasPtr->regionString != NULL) {
  1330.     ckfree(canvasPtr->regionString);
  1331.     }
  1332.     if (canvasPtr->cursor != None) {
  1333.     Tk_FreeCursor(canvasPtr->cursor);
  1334.     }
  1335.     ckfree((char *) canvasPtr);
  1336. }
  1337.  
  1338. /*
  1339.  *----------------------------------------------------------------------
  1340.  *
  1341.  * ConfigureCanvas --
  1342.  *
  1343.  *    This procedure is called to process an argv/argc list, plus
  1344.  *    the Tk option database, in order to configure (or
  1345.  *    reconfigure) a canvas widget.
  1346.  *
  1347.  * Results:
  1348.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  1349.  *    returned, then interp->result contains an error message.
  1350.  *
  1351.  * Side effects:
  1352.  *    Configuration information, such as colors, border width,
  1353.  *    etc. get set for canvasPtr;  old resources get freed,
  1354.  *    if there were any.
  1355.  *
  1356.  *----------------------------------------------------------------------
  1357.  */
  1358.  
  1359. static int
  1360. ConfigureCanvas(interp, canvasPtr, argc, argv, flags)
  1361.     Tcl_Interp *interp;        /* Used for error reporting. */
  1362.     register Tk_Canvas *canvasPtr;    /* Information about widget;  may or may
  1363.                  * not already have values for some fields. */
  1364.     int argc;            /* Number of valid entries in argv. */
  1365.     char **argv;        /* Arguments. */
  1366.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  1367. {
  1368.     XGCValues gcValues;
  1369.     GC new;
  1370.  
  1371.     if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
  1372.         argc, argv, (char *) canvasPtr, flags) != TCL_OK) {
  1373.     return TCL_ERROR;
  1374.     }
  1375.  
  1376.     /*
  1377.      * A few options need special processing, such as setting the
  1378.      * background from a 3-D border and creating a GC for copying
  1379.      * bits to the screen.
  1380.      */
  1381.  
  1382.     Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
  1383.  
  1384.     gcValues.function = GXcopy;
  1385.     gcValues.foreground = canvasPtr->bgColor->pixel;
  1386.     gcValues.graphics_exposures = False;
  1387.     new = Tk_GetGC(canvasPtr->tkwin,
  1388.         GCFunction|GCForeground|GCGraphicsExposures, &gcValues);
  1389.     if (canvasPtr->pixmapGC != None) {
  1390.     Tk_FreeGC(canvasPtr->pixmapGC);
  1391.     }
  1392.     canvasPtr->pixmapGC = new;
  1393.  
  1394.     /*
  1395.      * Reset the desired dimensions for the window.
  1396.      */
  1397.  
  1398.     Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width, canvasPtr->height);
  1399.  
  1400.     /*
  1401.      * Restart the cursor timing sequence in case the on-time or off-time
  1402.      * just changed.
  1403.      */
  1404.  
  1405.     if (canvasPtr->flags & GOT_FOCUS) {
  1406.     CanvasFocusProc((ClientData) canvasPtr, 1);
  1407.     }
  1408.  
  1409.     /*
  1410.      * Recompute the scroll region.
  1411.      */
  1412.  
  1413.     canvasPtr->scrollX1 = 0;
  1414.     canvasPtr->scrollY1 = 0;
  1415.     canvasPtr->scrollX2 = 0;
  1416.     canvasPtr->scrollY2 = 0;
  1417.     if (canvasPtr->regionString != NULL) {
  1418.     int argc2;
  1419.     char **argv2;
  1420.  
  1421.     if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
  1422.         &argc2, &argv2) != TCL_OK) {
  1423.         return TCL_ERROR;
  1424.     }
  1425.     if (argc2 != 4) {
  1426.         badRegion:
  1427.         Tcl_AppendResult(interp, "bad scrollRegion \"",
  1428.             canvasPtr->regionString, "\"", (char *) NULL);
  1429.         ckfree(canvasPtr->regionString);
  1430.         ckfree((char *) argv2);
  1431.         canvasPtr->regionString = NULL;
  1432.         return TCL_ERROR;
  1433.     }
  1434.     if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1435.             argv2[0], &canvasPtr->scrollX1) != TCL_OK)
  1436.         || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1437.             argv2[1], &canvasPtr->scrollY1) != TCL_OK)
  1438.         || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1439.             argv2[2], &canvasPtr->scrollX2) != TCL_OK)
  1440.         || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1441.             argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
  1442.         goto badRegion;
  1443.     }
  1444.     ckfree((char *) argv2);
  1445.     }
  1446.  
  1447.     /*
  1448.      * Reset the canvases origin (this is a no-op unless confine
  1449.      * mode has just been turned on or the scroll region has changed).
  1450.      */
  1451.  
  1452.     CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
  1453.     canvasPtr->flags |= UPDATE_SCROLLBARS;
  1454.     EventuallyRedrawArea(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin,
  1455.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  1456.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  1457.     return TCL_OK;
  1458. }
  1459.  
  1460. /*
  1461.  *--------------------------------------------------------------
  1462.  *
  1463.  * DisplayCanvas --
  1464.  *
  1465.  *    This procedure redraws the contents of a canvas window.
  1466.  *    It is invoked as a do-when-idle handler, so it only runs
  1467.  *    when there's nothing else for the application to do.
  1468.  *
  1469.  * Results:
  1470.  *    None.
  1471.  *
  1472.  * Side effects:
  1473.  *    Information appears on the screen.
  1474.  *
  1475.  *--------------------------------------------------------------
  1476.  */
  1477.  
  1478. static void
  1479. DisplayCanvas(clientData)
  1480.     ClientData clientData;    /* Information about widget. */
  1481. {
  1482.     register Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  1483.     register Tk_Window tkwin = canvasPtr->tkwin;
  1484.     register Tk_Item *itemPtr;
  1485.     Pixmap pixmap;
  1486.     int screenX1, screenX2, screenY1, screenY2;
  1487.  
  1488.     if (canvasPtr->tkwin == NULL) {
  1489.     return;
  1490.     }
  1491.     if (!Tk_IsMapped(tkwin)) {
  1492.     goto done;
  1493.     }
  1494.  
  1495.     /*
  1496.      * Choose a new current item if that is needed (this could cause
  1497.      * event handlers to be invoked).
  1498.      */
  1499.  
  1500.     while (canvasPtr->flags & REPICK_NEEDED) {
  1501.     Tk_Preserve((ClientData) canvasPtr);
  1502.     canvasPtr->flags &= ~REPICK_NEEDED;
  1503.     PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
  1504.     tkwin = canvasPtr->tkwin;
  1505.     Tk_Release((ClientData) canvasPtr);
  1506.     if (tkwin == NULL) {
  1507.         return;
  1508.     }
  1509.     }
  1510.  
  1511.     /*
  1512.      * Compute the intersection between the area that needs redrawing
  1513.      * and the area that's visible on the screen.
  1514.      */
  1515.  
  1516.     screenX1 = canvasPtr->xOrigin;
  1517.     screenY1 = canvasPtr->yOrigin;
  1518.     screenX2 = screenX1 + Tk_Width(tkwin);
  1519.     screenY2 = screenY1 + Tk_Height(tkwin);
  1520.     if (canvasPtr->redrawX1 > screenX1) {
  1521.     screenX1 = canvasPtr->redrawX1;
  1522.     }
  1523.     if (canvasPtr->redrawY1 > screenY1) {
  1524.     screenY1 = canvasPtr->redrawY1;
  1525.     }
  1526.     if (canvasPtr->redrawX2 < screenX2) {
  1527.     screenX2 = canvasPtr->redrawX2;
  1528.     }
  1529.     if (canvasPtr->redrawY2 < screenY2) {
  1530.     screenY2 = canvasPtr->redrawY2;
  1531.     }
  1532.     if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
  1533.     goto done;
  1534.     }
  1535.  
  1536.     /*
  1537.      * Redrawing is done in a temporary pixmap that is allocated
  1538.      * here and freed at the end of the procedure.  All drawing
  1539.      * is done to the pixmap, and the pixmap is copied to the
  1540.      * screen at the end of the procedure. The temporary pixmap
  1541.      * serves two purposes:
  1542.      *
  1543.      * 1. It provides a smoother visual effect (no clearing and
  1544.      *    gradual redraw will be visible to users).
  1545.      * 2. It allows us to redraw only the objects that overlap
  1546.      *    the redraw area.  Otherwise incorrect results could
  1547.      *      occur from redrawing things that stick outside of
  1548.      *      the redraw area (we'd have to redraw everything in
  1549.      *    order to make the overlaps look right).
  1550.      *
  1551.      * Some tricky points about the pixmap:
  1552.      *
  1553.      * 1. We only allocate a large enough pixmap to hold the
  1554.      *    area that has to be redisplayed.  This saves time in
  1555.      *    in the X server for large objects that cover much
  1556.      *    more than the area being redisplayed:  only the area
  1557.      *    of the pixmap will actually have to be redrawn.
  1558.      * 2. The origin of the pixmap is adjusted to an even multiple
  1559.      *    of 32 bits.  This is so that stipple patterns with a size
  1560.      *    of 8 or 16 or 32 bits will always line up when information
  1561.      *    is copied back to the screen.
  1562.      * 3. Some X servers (e.g. the one for DECstations) have troubles
  1563.      *    with characters that overlap an edge of the pixmap (on the
  1564.      *    DEC servers, as of 8/18/92, such characters are drawn one
  1565.      *    pixel too far to the right).  To handle this problem,
  1566.      *    make the pixmap a bit larger than is absolutely needed
  1567.      *    so that for normal-sized fonts the characters that ovelap
  1568.      *    the edge of the pixmap will be outside the area we care
  1569.      *    about.
  1570.      */
  1571.  
  1572.     canvasPtr->drawableXOrigin = (screenX1 - 30) & ~0x1f;
  1573.     canvasPtr->drawableYOrigin = (screenY1 - 30) & ~0x1f;
  1574.     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1575.     screenX2 + 30 - canvasPtr->drawableXOrigin,
  1576.     screenY2 + 30 - canvasPtr->drawableYOrigin,
  1577.     DefaultDepthOfScreen(Tk_Screen(tkwin)));
  1578.  
  1579.     /*
  1580.      * Clear the area to be redrawn.
  1581.      */
  1582.  
  1583.     XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
  1584.         screenX1 - canvasPtr->drawableXOrigin,
  1585.         screenY1 - canvasPtr->drawableYOrigin,
  1586.         (unsigned int) (screenX2 - screenX1),
  1587.         (unsigned int) (screenY2 - screenY1));
  1588.  
  1589.     /*
  1590.      * Scan through the item list, redrawing those items that need it.
  1591.      * An item must be redraw if either (a) it intersects the smaller
  1592.      * on-screen area or (b) it intersects the full canvas area and its
  1593.      * type requests that it be redrawn always (e.g. so subwindows can
  1594.      * be unmapped when they move off-screen).
  1595.      */
  1596.  
  1597.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1598.         itemPtr = itemPtr->nextPtr) {
  1599.     if ((itemPtr->x1 >= screenX2)
  1600.         || (itemPtr->y1 >= screenY2)
  1601.         || (itemPtr->x2 < screenX1)
  1602.         || (itemPtr->y2 < screenY1)) {
  1603.         if (!itemPtr->typePtr->alwaysRedraw
  1604.             || (itemPtr->x1 >= canvasPtr->redrawX2)
  1605.             || (itemPtr->y1 >= canvasPtr->redrawY2)
  1606.             || (itemPtr->x2 < canvasPtr->redrawX1)
  1607.             || (itemPtr->y2 < canvasPtr->redrawY1)) {
  1608.         continue;
  1609.         }
  1610.     }
  1611.     (*itemPtr->typePtr->displayProc)(canvasPtr, itemPtr, pixmap);
  1612.     }
  1613.  
  1614.     /*
  1615.      * Draw the window border.
  1616.      */
  1617.  
  1618.     if (canvasPtr->relief != TK_RELIEF_FLAT) {
  1619.     Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap,
  1620.         canvasPtr->bgBorder,
  1621.         canvasPtr->xOrigin - canvasPtr->drawableXOrigin,
  1622.         canvasPtr->yOrigin - canvasPtr->drawableYOrigin,
  1623.         Tk_Width(tkwin), Tk_Height(tkwin),
  1624.         canvasPtr->borderWidth, canvasPtr->relief);
  1625.     }
  1626.  
  1627.     /*
  1628.      * Copy from the temporary pixmap to the screen, then free up
  1629.      * the temporary pixmap.
  1630.      */
  1631.  
  1632.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
  1633.         canvasPtr->pixmapGC,
  1634.         screenX1 - canvasPtr->drawableXOrigin,
  1635.         screenY1 - canvasPtr->drawableYOrigin,
  1636.         screenX2 - screenX1, screenY2 - screenY1,
  1637.         screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
  1638.     XFreePixmap(Tk_Display(tkwin), pixmap);
  1639.  
  1640.     done:
  1641.     canvasPtr->flags &= ~REDRAW_PENDING;
  1642.     if (canvasPtr->flags & UPDATE_SCROLLBARS) {
  1643.     CanvasUpdateScrollbars(canvasPtr);
  1644.     }
  1645. }
  1646.  
  1647. /*
  1648.  *--------------------------------------------------------------
  1649.  *
  1650.  * CanvasEventProc --
  1651.  *
  1652.  *    This procedure is invoked by the Tk dispatcher for various
  1653.  *    events on canvases.
  1654.  *
  1655.  * Results:
  1656.  *    None.
  1657.  *
  1658.  * Side effects:
  1659.  *    When the window gets deleted, internal structures get
  1660.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1661.  *
  1662.  *--------------------------------------------------------------
  1663.  */
  1664.  
  1665. static void
  1666. CanvasEventProc(clientData, eventPtr)
  1667.     ClientData clientData;    /* Information about window. */
  1668.     XEvent *eventPtr;        /* Information about event. */
  1669. {
  1670.     Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  1671.  
  1672.     if (eventPtr->type == Expose) {
  1673.     int x, y;
  1674.  
  1675.     x = eventPtr->xexpose.x + canvasPtr->xOrigin;
  1676.     y = eventPtr->xexpose.y + canvasPtr->yOrigin;
  1677.     EventuallyRedrawArea(canvasPtr, x, y, x + eventPtr->xexpose.width,
  1678.         y + eventPtr->xexpose.height);
  1679.     } else if (eventPtr->type == DestroyNotify) {
  1680.     Tcl_DeleteCommand(canvasPtr->interp, Tk_PathName(canvasPtr->tkwin));
  1681.     canvasPtr->tkwin = NULL;
  1682.     if (canvasPtr->flags & REDRAW_PENDING) {
  1683.         Tk_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
  1684.     }
  1685.     Tk_EventuallyFree((ClientData) canvasPtr, DestroyCanvas);
  1686.     } else if (eventPtr->type == ConfigureNotify) {
  1687.     canvasPtr->flags |= UPDATE_SCROLLBARS;
  1688.  
  1689.     /*
  1690.      * The call below is needed in order to recenter the canvas if
  1691.      * it's confined and its scroll region is smaller than the window.
  1692.      */
  1693.  
  1694.     CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
  1695.     EventuallyRedrawArea(canvasPtr, 0, 0, Tk_Width(canvasPtr->tkwin),
  1696.         Tk_Height(canvasPtr->tkwin));
  1697.     }
  1698. }
  1699.  
  1700. /*
  1701.  *--------------------------------------------------------------
  1702.  *
  1703.  * EventuallyRedrawArea --
  1704.  *
  1705.  *    Arrange for part or all of a canvas widget to redrawn at
  1706.  *    the next convenient time in the future.
  1707.  *
  1708.  * Results:
  1709.  *    None.
  1710.  *
  1711.  * Side effects:
  1712.  *    The screen will eventually be refreshed.
  1713.  *
  1714.  *--------------------------------------------------------------
  1715.  */
  1716.  
  1717. static void
  1718. EventuallyRedrawArea(canvasPtr, x1, y1, x2, y2)
  1719.     register Tk_Canvas *canvasPtr;    /* Information about widget. */
  1720.     int x1, y1;                /* Upper left corner of area to
  1721.                      * redraw.  Pixels on edge are
  1722.                      * redrawn. */
  1723.     int x2, y2;                /* Lower right corner of area to
  1724.                      * redraw.  Pixels on edge are
  1725.                      * not redrawn. */
  1726. {
  1727.     if ((canvasPtr->tkwin == NULL) || !Tk_IsMapped(canvasPtr->tkwin)) {
  1728.     return;
  1729.     }
  1730.     if (canvasPtr->flags & REDRAW_PENDING) {
  1731.     if (x1 <= canvasPtr->redrawX1) {
  1732.         canvasPtr->redrawX1 = x1;
  1733.     }
  1734.     if (y1 <= canvasPtr->redrawY1) {
  1735.         canvasPtr->redrawY1 = y1;
  1736.     }
  1737.     if (x2 >= canvasPtr->redrawX2) {
  1738.         canvasPtr->redrawX2 = x2;
  1739.     }
  1740.     if (y2 >= canvasPtr->redrawY2) {
  1741.         canvasPtr->redrawY2 = y2;
  1742.     }
  1743.     } else {
  1744.     canvasPtr->redrawX1 = x1;
  1745.     canvasPtr->redrawY1 = y1;
  1746.     canvasPtr->redrawX2 = x2;
  1747.     canvasPtr->redrawY2 = y2;
  1748.     Tk_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
  1749.     canvasPtr->flags |= REDRAW_PENDING;
  1750.     }
  1751. }
  1752.  
  1753. /*
  1754.  *--------------------------------------------------------------
  1755.  *
  1756.  * Tk_CreateItemType --
  1757.  *
  1758.  *    This procedure may be invoked to add a new kind of canvas
  1759.  *    element to the core item types supported by Tk.
  1760.  *
  1761.  * Results:
  1762.  *    None.
  1763.  *
  1764.  * Side effects:
  1765.  *    From now on, the new item type will be useable in canvas
  1766.  *    widgets (e.g. typePtr->name can be used as the item type
  1767.  *    in "create" widget commands).  If there was already a
  1768.  *    type with the same name as in typePtr, it is replaced with
  1769.  *    the new type.
  1770.  *
  1771.  *--------------------------------------------------------------
  1772.  */
  1773.  
  1774. void
  1775. Tk_CreateItemType(typePtr)
  1776.     Tk_ItemType *typePtr;        /* Information about item type;
  1777.                      * storage must be statically
  1778.                      * allocated (must live forever). */
  1779. {
  1780.     if (typeList == NULL) {
  1781.     InitCanvas();
  1782.     }
  1783.     typePtr->nextPtr = typeList;
  1784.     typeList = typePtr;
  1785. }
  1786.  
  1787. /*
  1788.  *--------------------------------------------------------------
  1789.  *
  1790.  * InitCanvas --
  1791.  *
  1792.  *    This procedure is invoked to perform once-only-ever
  1793.  *    initialization for the module, such as setting up
  1794.  *    the type table.
  1795.  *
  1796.  * Results:
  1797.  *    None.
  1798.  *
  1799.  * Side effects:
  1800.  *    None.
  1801.  *
  1802.  *--------------------------------------------------------------
  1803.  */
  1804.  
  1805. static void
  1806. InitCanvas()
  1807. {
  1808.     if (typeList != NULL) {
  1809.     return;
  1810.     }
  1811.     typeList = &TkRectangleType;
  1812.     TkRectangleType.nextPtr = &TkTextType;
  1813.     TkTextType.nextPtr = &TkPolygonType;
  1814.     TkPolygonType.nextPtr = &TkOvalType;
  1815.     TkOvalType.nextPtr = &TkLineType;
  1816.     TkLineType.nextPtr = &TkWindowType;
  1817.     TkWindowType.nextPtr = &TkBitmapType;
  1818.     TkBitmapType.nextPtr = &TkArcType;
  1819.     TkArcType.nextPtr = NULL;
  1820.     allUid = Tk_GetUid("all");
  1821.     currentUid = Tk_GetUid("current");
  1822. }
  1823.  
  1824. /*
  1825.  *--------------------------------------------------------------
  1826.  *
  1827.  * StartTagSearch --
  1828.  *
  1829.  *    This procedure is called to initiate an enumeration of
  1830.  *    all items in a given canvas that contain a given tag.
  1831.  *
  1832.  * Results:
  1833.  *    The return value is a pointer to the first item in
  1834.  *    canvasPtr that matches tag, or NULL if there is no
  1835.  *    such item.  The information at *searchPtr is initialized
  1836.  *    such that successive calls to NextItem will return
  1837.  *    successive items that match tag.
  1838.  *
  1839.  * Side effects:
  1840.  *    SearchPtr is linked into a list of searches in progress
  1841.  *    on canvasPtr, so that elements can safely be deleted
  1842.  *    while the search is in progress.  EndTagSearch must be
  1843.  *    called at the end of the search to unlink searchPtr from
  1844.  *    this list.
  1845.  *
  1846.  *--------------------------------------------------------------
  1847.  */
  1848.  
  1849. static Tk_Item *
  1850. StartTagSearch(canvasPtr, tag, searchPtr)
  1851.     Tk_Canvas *canvasPtr;        /* Canvas whose items are to be
  1852.                      * searched. */
  1853.     char *tag;                /* String giving tag value. */
  1854.     TagSearch *searchPtr;        /* Record describing tag search;
  1855.                      * will be initialized here. */
  1856. {
  1857.     int id;
  1858.     register Tk_Item *itemPtr, *prevPtr;
  1859.     register Tk_Uid *tagPtr;
  1860.     register Tk_Uid uid;
  1861.     register int count;
  1862.  
  1863.     /*
  1864.      * Initialize the search.
  1865.      */
  1866.  
  1867.     searchPtr->canvasPtr = canvasPtr;
  1868.     searchPtr->searchOver = 0;
  1869.  
  1870.     /*
  1871.      * Find the first matching item in one of several ways. If the tag
  1872.      * is a number then it selects the single item with the matching
  1873.      * identifier.  In this case see if the item being requested is the
  1874.      * hot item, in which case the search can be skipped.
  1875.      */
  1876.  
  1877.     if (isdigit(*tag)) {
  1878.     char *end;
  1879.  
  1880.     numIdSearches++;
  1881.     id = strtoul(tag, &end, 0);
  1882.     if (*end == 0) {
  1883.         itemPtr = canvasPtr->hotPtr;
  1884.         prevPtr = canvasPtr->hotPrevPtr;
  1885.         if ((itemPtr == NULL) || (itemPtr->id != id) || (prevPtr == NULL)
  1886.             || (prevPtr->nextPtr != itemPtr)) {
  1887.         numSlowSearches++;
  1888.         for (prevPtr = NULL, itemPtr = canvasPtr->firstItemPtr;
  1889.             itemPtr != NULL;
  1890.             prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  1891.             if (itemPtr->id == id) {
  1892.             break;
  1893.             }
  1894.         }
  1895.         }
  1896.         searchPtr->prevPtr = prevPtr;
  1897.         searchPtr->searchOver = 1;
  1898.         canvasPtr->hotPtr = itemPtr;
  1899.         canvasPtr->hotPrevPtr = prevPtr;
  1900.         return itemPtr;
  1901.     }
  1902.     }
  1903.  
  1904.     searchPtr->tag = uid = Tk_GetUid(tag);
  1905.     if (uid == allUid) {
  1906.  
  1907.     /*
  1908.      * All items match.
  1909.      */
  1910.  
  1911.     searchPtr->tag = NULL;
  1912.     searchPtr->prevPtr = NULL;
  1913.     searchPtr->currentPtr = canvasPtr->firstItemPtr;
  1914.     return canvasPtr->firstItemPtr;
  1915.     }
  1916.  
  1917.     /*
  1918.      * None of the above.  Search for an item with a matching tag.
  1919.      */
  1920.  
  1921.     for (prevPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1922.         prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  1923.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  1924.         count > 0; tagPtr++, count--) {
  1925.         if (*tagPtr == uid) {
  1926.         searchPtr->prevPtr = prevPtr;
  1927.         searchPtr->currentPtr = itemPtr;
  1928.         return itemPtr;
  1929.         }
  1930.     }
  1931.     }
  1932.     searchPtr->prevPtr = prevPtr;
  1933.     searchPtr->searchOver = 1;
  1934.     return NULL;
  1935. }
  1936.  
  1937. /*
  1938.  *--------------------------------------------------------------
  1939.  *
  1940.  * NextItem --
  1941.  *
  1942.  *    This procedure returns successive items that match a given
  1943.  *    tag;  it should be called only after StartTagSearch has been
  1944.  *    used to begin a search.
  1945.  *
  1946.  * Results:
  1947.  *    The return value is a pointer to the next item that matches
  1948.  *    the tag specified to StartTagSearch, or NULL if no such
  1949.  *    item exists.  *SearchPtr is updated so that the next call
  1950.  *    to this procedure will return the next item.
  1951.  *
  1952.  * Side effects:
  1953.  *    None.
  1954.  *
  1955.  *--------------------------------------------------------------
  1956.  */
  1957.  
  1958. static Tk_Item *
  1959. NextItem(searchPtr)
  1960.     TagSearch *searchPtr;        /* Record describing search in
  1961.                      * progress. */
  1962. {
  1963.     register Tk_Item *itemPtr, *prevPtr;
  1964.     register int count;
  1965.     register Tk_Uid uid;
  1966.     register Tk_Uid *tagPtr;
  1967.  
  1968.     /*
  1969.      * Find next item in list (this may not actually be a suitable
  1970.      * one to return), and return if there are no items left.
  1971.      */
  1972.  
  1973.     prevPtr = searchPtr->prevPtr;
  1974.     if (prevPtr == NULL) {
  1975.     itemPtr = searchPtr->canvasPtr->firstItemPtr;
  1976.     } else {
  1977.     itemPtr = prevPtr->nextPtr;
  1978.     }
  1979.     if ((itemPtr == NULL) || (searchPtr->searchOver)) {
  1980.     searchPtr->searchOver = 1;
  1981.     return NULL;
  1982.     }
  1983.     if (itemPtr != searchPtr->currentPtr) {
  1984.     /*
  1985.      * The structure of the list has changed.  Probably the
  1986.      * previously-returned item was removed from the list.
  1987.      * In this case, don't advance prevPtr;  just return
  1988.      * its new successor (i.e. do nothing here).
  1989.      */
  1990.     } else {
  1991.     prevPtr = itemPtr;
  1992.     itemPtr = prevPtr->nextPtr;
  1993.     }
  1994.  
  1995.     /*
  1996.      * Handle special case of "all" search by returning next item.
  1997.      */
  1998.  
  1999.     uid = searchPtr->tag;
  2000.     if (uid == NULL) {
  2001.     searchPtr->prevPtr = prevPtr;
  2002.     searchPtr->currentPtr = itemPtr;
  2003.     return itemPtr;
  2004.     }
  2005.  
  2006.     /*
  2007.      * Look for an item with a particular tag.
  2008.      */
  2009.  
  2010.     for ( ; itemPtr != NULL; prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  2011.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  2012.         count > 0; tagPtr++, count--) {
  2013.         if (*tagPtr == uid) {
  2014.         searchPtr->prevPtr = prevPtr;
  2015.         searchPtr->currentPtr = itemPtr;
  2016.         return itemPtr;
  2017.         }
  2018.     }
  2019.     }
  2020.     searchPtr->prevPtr = prevPtr;
  2021.     searchPtr->searchOver = 1;
  2022.     return NULL;
  2023. }
  2024.  
  2025. /*
  2026.  *--------------------------------------------------------------
  2027.  *
  2028.  * DoItem --
  2029.  *
  2030.  *    This is a utility procedure called by FindItems.  It
  2031.  *    either adds itemPtr's id to the result forming in interp,
  2032.  *    or it adds a new tag to itemPtr, depending on the value
  2033.  *    of tag.
  2034.  *
  2035.  * Results:
  2036.  *    None.
  2037.  *
  2038.  * Side effects:
  2039.  *    If tag is NULL then itemPtr's id is added as a list element
  2040.  *    to interp->result;  otherwise tag is added to itemPtr's
  2041.  *    list of tags.
  2042.  *
  2043.  *--------------------------------------------------------------
  2044.  */
  2045.  
  2046. static void
  2047. DoItem(interp, itemPtr, tag)
  2048.     Tcl_Interp *interp;            /* Interpreter in which to (possibly)
  2049.                      * record item id. */
  2050.     register Tk_Item *itemPtr;        /* Item to (possibly) modify. */
  2051.     Tk_Uid tag;                /* Tag to add to those already
  2052.                      * present for item, or NULL. */
  2053. {
  2054.     register Tk_Uid *tagPtr;
  2055.     register int count;
  2056.  
  2057.     /*
  2058.      * Handle the "add-to-result" case and return, if appropriate.
  2059.      */
  2060.  
  2061.     if (tag == NULL) {
  2062.     char msg[30];
  2063.     sprintf(msg, "%d", itemPtr->id);
  2064.     Tcl_AppendElement(interp, msg, 0);
  2065.     return;
  2066.     }
  2067.  
  2068.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  2069.         count > 0; tagPtr++, count--) {
  2070.     if (tag == *tagPtr) {
  2071.         return;
  2072.     }
  2073.     }
  2074.  
  2075.     /*
  2076.      * Grow the tag space if there's no more room left in the current
  2077.      * block.
  2078.      */
  2079.  
  2080.     if (itemPtr->tagSpace == itemPtr->numTags) {
  2081.     Tk_Uid *newTagPtr;
  2082.  
  2083.     itemPtr->tagSpace += 5;
  2084.     newTagPtr = (Tk_Uid *) ckalloc((unsigned)
  2085.         (itemPtr->tagSpace * sizeof(Tk_Uid)));
  2086.     memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
  2087.         (itemPtr->numTags * sizeof(Tk_Uid)));
  2088.     if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  2089.         ckfree((char *) itemPtr->tagPtr);
  2090.     }
  2091.     itemPtr->tagPtr = newTagPtr;
  2092.     tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
  2093.     }
  2094.  
  2095.     /*
  2096.      * Add in the new tag.
  2097.      */
  2098.  
  2099.     *tagPtr = tag;
  2100.     itemPtr->numTags++;
  2101. }
  2102.  
  2103. /*
  2104.  *--------------------------------------------------------------
  2105.  *
  2106.  * FindItems --
  2107.  *
  2108.  *    This procedure does all the work of implementing the
  2109.  *    "find" and "addtag" options of the canvas widget command,
  2110.  *    which locate items that have certain features (location,
  2111.  *    tags, position in display list, etc.).
  2112.  *
  2113.  * Results:
  2114.  *    A standard Tcl return value.  If newTag is NULL, then a
  2115.  *    list of ids from all the items that match argc/argv is
  2116.  *    returned in interp->result.  If newTag is NULL, then
  2117.  *    the normal interp->result is an empty string.  If an error
  2118.  *    occurs, then interp->result will hold an error message.
  2119.  *
  2120.  * Side effects:
  2121.  *    If newTag is non-NULL, then all the items that match the
  2122.  *    information in argc/argv have that tag added to their
  2123.  *    lists of tags.
  2124.  *
  2125.  *--------------------------------------------------------------
  2126.  */
  2127.  
  2128. static int
  2129. FindItems(interp, canvasPtr, argc, argv, newTag, cmdName, option)
  2130.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  2131.     Tk_Canvas *canvasPtr;        /* Canvas whose items are to be
  2132.                      * searched. */
  2133.     int argc;                /* Number of entries in argv.  Must be
  2134.                      * greater than zero. */
  2135.     char **argv;            /* Arguments that describe what items
  2136.                      * to search for (see user doc on
  2137.                      * "find" and "addtag" options). */
  2138.     char *newTag;            /* If non-NULL, gives new tag to set
  2139.                      * on all found items;  if NULL, then
  2140.                      * ids of found items are returned
  2141.                      * in interp->result. */
  2142.     char *cmdName;            /* Name of original Tcl command, for
  2143.                      * use in error messages. */
  2144.     char *option;            /* For error messages:  gives option
  2145.                      * from Tcl command and other stuff
  2146.                      * up to what's in argc/argv. */
  2147. {
  2148.     char c;
  2149.     int length;
  2150.     TagSearch search;
  2151.     register Tk_Item *itemPtr;
  2152.     Tk_Uid uid;
  2153.  
  2154.     if (newTag != NULL) {
  2155.     uid = Tk_GetUid(newTag);
  2156.     } else {
  2157.     uid = NULL;
  2158.     }
  2159.     c = argv[0][0];
  2160.     length = strlen(argv[0]);
  2161.     if ((c == 'a') && (strncmp(argv[0], "above", length) == 0)
  2162.         && (length >= 2)) {
  2163.     Tk_Item *lastPtr = NULL;
  2164.     if (argc != 2) {
  2165.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2166.             cmdName, option, " above tagOrId", (char *) NULL);
  2167.         return TCL_ERROR;
  2168.     }
  2169.     for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
  2170.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  2171.         lastPtr = itemPtr;
  2172.     }
  2173.     if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
  2174.         DoItem(interp, lastPtr->nextPtr, uid);
  2175.     }
  2176.     } else if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
  2177.         && (length >= 2)) {
  2178.     if (argc != 1) {
  2179.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2180.             cmdName, option, " all", (char *) NULL);
  2181.         return TCL_ERROR;
  2182.     }
  2183.  
  2184.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  2185.         itemPtr = itemPtr->nextPtr) {
  2186.         DoItem(interp, itemPtr, uid);
  2187.     }
  2188.     } else if ((c == 'b') && (strncmp(argv[0], "below", length) == 0)) {
  2189.     if (argc != 2) {
  2190.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2191.             cmdName, option, " below tagOrId", (char *) NULL);
  2192.         return TCL_ERROR;
  2193.     }
  2194.     itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
  2195.     if (search.prevPtr != NULL) {
  2196.         DoItem(interp, search.prevPtr, uid);
  2197.     }
  2198.     } else if ((c == 'c') && (strncmp(argv[0], "closest", length) == 0)) {
  2199.     double closestDist;
  2200.     Tk_Item *startPtr, *closestPtr;
  2201.     double coords[2], halo;
  2202.     int x1, y1, x2, y2;
  2203.  
  2204.     if ((argc < 3) || (argc > 5)) {
  2205.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2206.             cmdName, option, " closest x y ?halo? ?start?",
  2207.             (char *) NULL);
  2208.         return TCL_ERROR;
  2209.     }
  2210.     if ((TkGetCanvasCoord(canvasPtr, argv[1], &coords[0]) != TCL_OK)
  2211.         || (TkGetCanvasCoord(canvasPtr, argv[2], &coords[1])
  2212.         != TCL_OK)) {
  2213.         return TCL_ERROR;
  2214.     }
  2215.     if (argc > 3) {
  2216.         if (TkGetCanvasCoord(canvasPtr, argv[3], &halo) != TCL_OK) {
  2217.         return TCL_ERROR;
  2218.         }
  2219.         if (halo < 0.0) {
  2220.         Tcl_AppendResult(interp, "can't have negative halo value \"",
  2221.             argv[3], "\"", (char *) NULL);
  2222.         return TCL_ERROR;
  2223.         }
  2224.     } else {
  2225.         halo = 0.0;
  2226.     }
  2227.  
  2228.     /*
  2229.      * Find the item at which to start the search.
  2230.      */
  2231.  
  2232.     startPtr = canvasPtr->firstItemPtr;
  2233.     if (argc == 5) {
  2234.         itemPtr = StartTagSearch(canvasPtr, argv[4], &search);
  2235.         if (itemPtr != NULL) {
  2236.         startPtr = itemPtr;
  2237.         }
  2238.     }
  2239.  
  2240.     /*
  2241.      * The code below is optimized so that it can eliminate most
  2242.      * items without having to call their item-specific procedures.
  2243.      * This is done by keeping a bounding box (x1, y1, x2, y2) that
  2244.      * an item's bbox must overlap if the item is to have any
  2245.      * chance of being closer than the closest so far.
  2246.      */
  2247.  
  2248.     itemPtr = startPtr;
  2249.     if (itemPtr == NULL) {
  2250.         return TCL_OK;
  2251.     }
  2252.     closestDist = (*itemPtr->typePtr->pointProc)(canvasPtr,
  2253.         itemPtr, coords) - halo;
  2254.     if (closestDist < 0.0) {
  2255.         closestDist = 0.0;
  2256.     }
  2257.     while (1) {
  2258.         double newDist;
  2259.  
  2260.         /*
  2261.          * Update the bounding box using itemPtr, which is the
  2262.          * new closest item.
  2263.          */
  2264.  
  2265.         x1 = (coords[0] - closestDist - halo - 1);
  2266.         y1 = (coords[1] - closestDist - halo - 1);
  2267.         x2 = (coords[0] + closestDist + halo + 1);
  2268.         y2 = (coords[1] + closestDist + halo + 1);
  2269.         closestPtr = itemPtr;
  2270.  
  2271.         /*
  2272.          * Search for an item that beats the current closest one.
  2273.          * Work circularly through the canvas's item list until
  2274.          * getting back to the starting item.
  2275.          */
  2276.  
  2277.         while (1) {
  2278.         itemPtr = itemPtr->nextPtr;
  2279.         if (itemPtr == NULL) {
  2280.             itemPtr = canvasPtr->firstItemPtr;
  2281.         }
  2282.         if (itemPtr == startPtr) {
  2283.             DoItem(interp, closestPtr, uid);
  2284.             return TCL_OK;
  2285.         }
  2286.         if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
  2287.             || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
  2288.             continue;
  2289.         }
  2290.         newDist = (*itemPtr->typePtr->pointProc)(canvasPtr,
  2291.             itemPtr, coords) - halo;
  2292.         if (newDist < 0.0) {
  2293.             newDist = 0.0;
  2294.         }
  2295.         if (newDist <= closestDist) {
  2296.             closestDist = newDist;
  2297.             break;
  2298.         }
  2299.         }
  2300.     }
  2301.     } else if ((c == 'e') && (strncmp(argv[0], "enclosed", length) == 0)) {
  2302.     if (argc != 5) {
  2303.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2304.             cmdName, option, " enclosed x1 y1 x2 y2", (char *) NULL);
  2305.         return TCL_ERROR;
  2306.     }
  2307.     return FindArea(interp, canvasPtr, argv+1, uid, 1);
  2308.     } else if ((c == 'o') && (strncmp(argv[0], "overlapping", length) == 0)) {
  2309.     if (argc != 5) {
  2310.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2311.             cmdName, option, " overlapping x1 y1 x2 y2",
  2312.             (char *) NULL);
  2313.         return TCL_ERROR;
  2314.     }
  2315.     return FindArea(interp, canvasPtr, argv+1, uid, 0);
  2316.     } else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
  2317.     if (argc != 2) {
  2318.         Tcl_AppendResult(interp, "wrong # args:  must be \"",
  2319.             cmdName, option, " withtag tagOrId", (char *) NULL);
  2320.         return TCL_ERROR;
  2321.     }
  2322.     for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
  2323.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  2324.         DoItem(interp, itemPtr, uid);
  2325.     }
  2326.     } else  {
  2327.     Tcl_AppendResult(interp, "bad search command \"", argv[0],
  2328.         "\": must be above, all, below, closest, enclosed, ",
  2329.         "overlapping, or withtag", (char *) NULL);
  2330.     return TCL_ERROR;
  2331.     }
  2332.     return TCL_OK;
  2333. }
  2334.  
  2335. /*
  2336.  *--------------------------------------------------------------
  2337.  *
  2338.  * FindArea --
  2339.  *
  2340.  *    This procedure implements area searches for the "find"
  2341.  *    and "addtag" options.
  2342.  *
  2343.  * Results:
  2344.  *    A standard Tcl return value.  If newTag is NULL, then a
  2345.  *    list of ids from all the items overlapping or enclosed
  2346.  *    by the rectangle given by argc is returned in interp->result.
  2347.  *    If newTag is NULL, then the normal interp->result is an
  2348.  *    empty string.  If an error occurs, then interp->result will
  2349.  *    hold an error message.
  2350.  *
  2351.  * Side effects:
  2352.  *    If uid is non-NULL, then all the items overlapping
  2353.  *    or enclosed by the area in argv have that tag added to
  2354.  *    their lists of tags.
  2355.  *
  2356.  *--------------------------------------------------------------
  2357.  */
  2358.  
  2359. static int
  2360. FindArea(interp, canvasPtr, argv, uid, enclosed)
  2361.     Tcl_Interp *interp;            /* Interpreter for error reporting
  2362.                      * and result storing. */
  2363.     Tk_Canvas *canvasPtr;        /* Canvas whose items are to be
  2364.                      * searched. */
  2365.     char **argv;            /* Array of four arguments that
  2366.                      * give the coordinates of the
  2367.                      * rectangular area to search. */
  2368.     Tk_Uid uid;                /* If non-NULL, gives new tag to set
  2369.                      * on all found items;  if NULL, then
  2370.                      * ids of found items are returned
  2371.                      * in interp->result. */
  2372.     int enclosed;            /* 0 means overlapping or enclosed
  2373.                      * items are OK, 1 means only enclosed
  2374.                      * items are OK. */
  2375. {
  2376.     double rect[4], tmp;
  2377.     int x1, y1, x2, y2;
  2378.     register Tk_Item *itemPtr;
  2379.  
  2380.     if ((TkGetCanvasCoord(canvasPtr, argv[0], &rect[0]) != TCL_OK)
  2381.         || (TkGetCanvasCoord(canvasPtr, argv[1], &rect[1]) != TCL_OK)
  2382.         || (TkGetCanvasCoord(canvasPtr, argv[2], &rect[2]) != TCL_OK)
  2383.         || (TkGetCanvasCoord(canvasPtr, argv[3], &rect[3]) != TCL_OK)) {
  2384.     return TCL_ERROR;
  2385.     }
  2386.     if (rect[0] > rect[2]) {
  2387.     tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
  2388.     }
  2389.     if (rect[1] > rect[3]) {
  2390.     tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
  2391.     }
  2392.  
  2393.     /*
  2394.      * Use an integer bounding box for a quick test, to avoid
  2395.      * calling item-specific code except for items that are close.
  2396.      */
  2397.  
  2398.     x1 = (rect[0]-1.0);
  2399.     y1 = (rect[1]-1.0);
  2400.     x2 = (rect[2]+1.0);
  2401.     y2 = (rect[3]+1.0);
  2402.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  2403.         itemPtr = itemPtr->nextPtr) {
  2404.     if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
  2405.         || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
  2406.         continue;
  2407.     }
  2408.     if ((*itemPtr->typePtr->areaProc)(canvasPtr, itemPtr, rect)
  2409.         >= enclosed) {
  2410.         DoItem(interp, itemPtr, uid);
  2411.     }
  2412.     }
  2413.     return TCL_OK;
  2414. }
  2415.  
  2416. /*
  2417.  *--------------------------------------------------------------
  2418.  *
  2419.  * RelinkItems --
  2420.  *
  2421.  *    Move one or more items to a different place in the
  2422.  *    display order for a canvas.
  2423.  *
  2424.  * Results:
  2425.  *    None.
  2426.  *
  2427.  * Side effects:
  2428.  *    The items identified by "tag" are moved so that they
  2429.  *    are all together in the display list and immediately
  2430.  *    after prevPtr.  The order of the moved items relative
  2431.  *    to each other is not changed.
  2432.  *
  2433.  *--------------------------------------------------------------
  2434.  */
  2435.  
  2436. static void
  2437. RelinkItems(canvasPtr, tag, prevPtr)
  2438.     Tk_Canvas *canvasPtr;    /* Canvas to be modified. */
  2439.     char *tag;            /* Tag identifying items to be moved
  2440.                  * in the redisplay list. */
  2441.     Tk_Item *prevPtr;        /* Reposition the items so that they
  2442.                  * go just after this item (NULL means
  2443.                  * put at beginning of list). */
  2444. {
  2445.     register Tk_Item *itemPtr;
  2446.     TagSearch search;
  2447.     Tk_Item *firstMovePtr, *lastMovePtr;
  2448.  
  2449.     /*
  2450.      * Find all of the items to be moved and remove them from
  2451.      * the list, making an auxiliary list running from firstMovePtr
  2452.      * to lastMovePtr.  Record their areas for redisplay.
  2453.      */
  2454.  
  2455.     firstMovePtr = lastMovePtr = NULL;
  2456.     for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
  2457.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  2458.     if (itemPtr == prevPtr) {
  2459.         /*
  2460.          * Item after which insertion is to occur is being
  2461.          * moved!  Switch to insert after its predecessor.
  2462.          */
  2463.  
  2464.         prevPtr = search.prevPtr;
  2465.     }
  2466.     if (search.prevPtr == NULL) {
  2467.         canvasPtr->firstItemPtr = itemPtr->nextPtr;
  2468.     } else {
  2469.         search.prevPtr->nextPtr = itemPtr->nextPtr;
  2470.     }
  2471.     if (canvasPtr->lastItemPtr == itemPtr) {
  2472.         canvasPtr->lastItemPtr = search.prevPtr;
  2473.     }
  2474.     if (firstMovePtr == NULL) {
  2475.         firstMovePtr = itemPtr;
  2476.     } else {
  2477.         lastMovePtr->nextPtr = itemPtr;
  2478.     }
  2479.     lastMovePtr = itemPtr;
  2480.     EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  2481.         itemPtr->x2, itemPtr->y2);
  2482.     canvasPtr->flags |= REPICK_NEEDED;
  2483.     }
  2484.  
  2485.     /*
  2486.      * Insert the list of to-be-moved items back into the canvas's
  2487.      * at the desired position.
  2488.      */
  2489.  
  2490.     if (firstMovePtr == NULL) {
  2491.     return;
  2492.     }
  2493.     if (prevPtr == NULL) {
  2494.     lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
  2495.     canvasPtr->firstItemPtr = firstMovePtr;
  2496.     } else {
  2497.     lastMovePtr->nextPtr = prevPtr->nextPtr;
  2498.     prevPtr->nextPtr = firstMovePtr;
  2499.     }
  2500.     if (canvasPtr->lastItemPtr == prevPtr) {
  2501.     canvasPtr->lastItemPtr = lastMovePtr;
  2502.     }
  2503. }
  2504.  
  2505. /*
  2506.  *--------------------------------------------------------------
  2507.  *
  2508.  * CanvasBindProc --
  2509.  *
  2510.  *    This procedure is invoked by the Tk dispatcher to handle
  2511.  *    events associated with bindings on items.
  2512.  *
  2513.  * Results:
  2514.  *    None.
  2515.  *
  2516.  * Side effects:
  2517.  *    Depends on the command invoked as part of the binding
  2518.  *    (if there was any).
  2519.  *
  2520.  *--------------------------------------------------------------
  2521.  */
  2522.  
  2523. static void
  2524. CanvasBindProc(clientData, eventPtr)
  2525.     ClientData clientData;        /* Pointer to canvas structure. */
  2526.     XEvent *eventPtr;            /* Pointer to X event that just
  2527.                      * happened. */
  2528. {
  2529.     Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  2530.     int repick  = 0;
  2531.  
  2532.     Tk_Preserve((ClientData) canvasPtr);
  2533.  
  2534.     /*
  2535.      * This code simulates grabs for mouse buttons by refusing to
  2536.      * pick a new current item between the time a mouse button goes
  2537.      * down and the time when the last mouse button is released is
  2538.      * released again.
  2539.      */
  2540.  
  2541.     if (eventPtr->type == ButtonPress) {
  2542.     canvasPtr->flags |= BUTTON_DOWN;
  2543.     } else if (eventPtr->type == ButtonRelease) {
  2544.     int mask;
  2545.  
  2546.     switch (eventPtr->xbutton.button) {
  2547.         case Button1:
  2548.         mask = Button1Mask;
  2549.         break;
  2550.         case Button2:
  2551.         mask = Button2Mask;
  2552.         break;
  2553.         case Button3:
  2554.         mask = Button3Mask;
  2555.         break;
  2556.         case Button4:
  2557.         mask = Button4Mask;
  2558.         break;
  2559.         case Button5:
  2560.         mask = Button5Mask;
  2561.         break;
  2562.         default:
  2563.         mask = 0;
  2564.         break;
  2565.     }
  2566.     if ((eventPtr->xbutton.state & (Button1Mask|Button2Mask
  2567.         |Button3Mask|Button4Mask|Button5Mask)) == mask) {
  2568.         canvasPtr->flags &= ~BUTTON_DOWN;
  2569.         repick = 1;
  2570.     }
  2571.     } else if ((eventPtr->type == EnterNotify)
  2572.         || (eventPtr->type == LeaveNotify)) {
  2573.     PickCurrentItem(canvasPtr, eventPtr);
  2574.     goto done;
  2575.     } else if (eventPtr->type == MotionNotify) {
  2576.     PickCurrentItem(canvasPtr, eventPtr);
  2577.     }
  2578.     CanvasDoEvent(canvasPtr, eventPtr);
  2579.     if (repick) {
  2580.     unsigned int oldState;
  2581.  
  2582.     oldState = eventPtr->xbutton.state;
  2583.     eventPtr->xbutton.state &= ~(Button1Mask|Button2Mask
  2584.         |Button3Mask|Button4Mask|Button5Mask);
  2585.     PickCurrentItem(canvasPtr, eventPtr);
  2586.     eventPtr->xbutton.state = oldState;
  2587.     }
  2588.  
  2589.     done:
  2590.     Tk_Release((ClientData) canvasPtr);
  2591. }
  2592.  
  2593. /*
  2594.  *--------------------------------------------------------------
  2595.  *
  2596.  * PickCurrentItem --
  2597.  *
  2598.  *    Find the topmost item in a canvas that contains a given
  2599.  *    location and mark the the current item.  If the current
  2600.  *    item has changed, generate a fake exit event on the old
  2601.  *    current item and a fake enter event on the new current
  2602.  *    item.
  2603.  *
  2604.  * Results:
  2605.  *    None.
  2606.  *
  2607.  * Side effects:
  2608.  *    The current item for canvasPtr may change.  If it does,
  2609.  *    then the commands associated with item entry and exit
  2610.  *    could do just about anything.
  2611.  *
  2612.  *--------------------------------------------------------------
  2613.  */
  2614.  
  2615. static void
  2616. PickCurrentItem(canvasPtr, eventPtr)
  2617.     register Tk_Canvas *canvasPtr;    /* Canvas pointer in which to select
  2618.                      * current item. */
  2619.     XEvent *eventPtr;            /* Event describing location of
  2620.                      * mouse cursor.  Must be EnterWindow,
  2621.                      * LeaveWindow, ButtonRelease, or
  2622.                      * MotionNotify. */
  2623. {
  2624.     Tk_Item *closestPtr = NULL;
  2625.  
  2626.     /*
  2627.      * If a button is down, then don't do anything at all;  we'll be
  2628.      * called again when all buttons are up, and we can repick then.
  2629.      * This implements a form of mouse grabbing for canvases.
  2630.      */
  2631.  
  2632.     if (canvasPtr->flags & BUTTON_DOWN) {
  2633.     return;
  2634.     }
  2635.  
  2636.     /*
  2637.      * Save information about this event in the canvas.  The event in
  2638.      * the canvas is used for two purposes:
  2639.      *
  2640.      * 1. Event bindings: if the current item changes, fake events are
  2641.      *    generated to allow item-enter and item-leave bindings to trigger.
  2642.      * 2. Reselection: if the current item gets deleted, can use the
  2643.      *    saved event to find a new current item.
  2644.      * Translate MotionNotify events into EnterNotify events, since that's
  2645.      * what gets reported to item handlers.
  2646.      */
  2647.  
  2648.     if (eventPtr != &canvasPtr->pickEvent) {
  2649.     if ((eventPtr->type == MotionNotify)
  2650.         || (eventPtr->type == ButtonRelease)) {
  2651.         canvasPtr->pickEvent.xcrossing.type = EnterNotify;
  2652.         canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
  2653.         canvasPtr->pickEvent.xcrossing.send_event
  2654.             = eventPtr->xmotion.send_event;
  2655.         canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
  2656.         canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
  2657.         canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
  2658.         canvasPtr->pickEvent.xcrossing.subwindow = None;
  2659.         canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
  2660.         canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
  2661.         canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
  2662.         canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
  2663.         canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
  2664.         canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
  2665.         canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
  2666.         canvasPtr->pickEvent.xcrossing.same_screen
  2667.             = eventPtr->xmotion.same_screen;
  2668.         canvasPtr->pickEvent.xcrossing.focus = False;
  2669.         canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
  2670.     } else  {
  2671.         canvasPtr->pickEvent = *eventPtr;
  2672.     }
  2673.     }
  2674.  
  2675.     /*
  2676.      * A LeaveNotify event automatically means that there's no current
  2677.      * object, so the rest of the code below can be skipped.
  2678.      */
  2679.  
  2680.     if (canvasPtr->pickEvent.type != LeaveNotify) {
  2681.     int x1, y1, x2, y2;
  2682.     double coords[2];
  2683.     register Tk_Item *itemPtr;
  2684.  
  2685.     coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
  2686.     coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
  2687.     x1 = coords[0] - canvasPtr->closeEnough;
  2688.     y1 = coords[1] - canvasPtr->closeEnough;
  2689.     x2 = coords[0] + canvasPtr->closeEnough;
  2690.     y2 = coords[1] + canvasPtr->closeEnough;
  2691.     
  2692.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  2693.         itemPtr = itemPtr->nextPtr) {
  2694.         if ((itemPtr->x1 >= x2) || (itemPtr->x2 < x1)
  2695.             || (itemPtr->y1 >= y2) || (itemPtr->y2 < y1)) {
  2696.         continue;
  2697.         }
  2698.         if ((*itemPtr->typePtr->pointProc)(canvasPtr,
  2699.             itemPtr, coords) <= canvasPtr->closeEnough) {
  2700.         closestPtr = itemPtr;
  2701.         }
  2702.     }
  2703.     }
  2704.  
  2705.     /*
  2706.      * Simulate a LeaveNotify event on the previous current item and
  2707.      * an EnterNotify event on the new current item.  Remove the "current"
  2708.      * tag from the previous current item and place it on the new current
  2709.      * item.
  2710.      */
  2711.  
  2712.     if (closestPtr == canvasPtr->currentItemPtr) {
  2713.     return;
  2714.     }
  2715.     if (canvasPtr->currentItemPtr != NULL) {
  2716.     XEvent event;
  2717.     Tk_Item *itemPtr = canvasPtr->currentItemPtr;
  2718.     int i;
  2719.  
  2720.     event = canvasPtr->pickEvent;
  2721.     event.type = LeaveNotify;
  2722.     CanvasDoEvent(canvasPtr, &event);
  2723.     for (i = itemPtr->numTags-1; i >= 0; i--) {
  2724.         if (itemPtr->tagPtr[i] == currentUid) {
  2725.         itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
  2726.         itemPtr->numTags--;
  2727.         break;
  2728.         }
  2729.     }
  2730.     }
  2731.     canvasPtr->currentItemPtr = closestPtr;
  2732.     if (canvasPtr->currentItemPtr != NULL) {
  2733.     XEvent event;
  2734.  
  2735.     DoItem((Tcl_Interp *) NULL, closestPtr, currentUid);
  2736.     event = canvasPtr->pickEvent;
  2737.     event.type = EnterNotify;
  2738.     CanvasDoEvent(canvasPtr, &event);
  2739.     }
  2740. }
  2741.  
  2742. /*
  2743.  *--------------------------------------------------------------
  2744.  *
  2745.  * CanvasDoEvent --
  2746.  *
  2747.  *    This procedure is called to invoke binding processing
  2748.  *    for a new event that is associated with the current item
  2749.  *    for a canvas.
  2750.  *
  2751.  * Results:
  2752.  *    None.
  2753.  *
  2754.  * Side effects:
  2755.  *    Depends on the bindings for the canvas.
  2756.  *
  2757.  *--------------------------------------------------------------
  2758.  */
  2759.  
  2760. static void
  2761. CanvasDoEvent(canvasPtr, eventPtr)
  2762.     Tk_Canvas *canvasPtr;        /* Canvas widget in which event
  2763.                      * occurred. */
  2764.     XEvent *eventPtr;            /* Real or simulated X event that
  2765.                      * is to be processed. */
  2766. {
  2767. #define NUM_STATIC 3
  2768.     ClientData staticObjects[NUM_STATIC];
  2769.     ClientData *objectPtr;
  2770.     int numObjects, i;
  2771.     register Tk_Item *itemPtr;
  2772.  
  2773.     if (canvasPtr->bindingTable == NULL) {
  2774.     return;
  2775.     }
  2776.  
  2777.     itemPtr = canvasPtr->currentItemPtr;
  2778.     if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
  2779.     itemPtr = canvasPtr->focusItemPtr;
  2780.     }
  2781.     if (itemPtr == NULL) {
  2782.     return;
  2783.     }
  2784.  
  2785.     /*
  2786.      * Set up an array with all the relevant objects for processing
  2787.      * this event.  The relevant objects are (a) the event's item,
  2788.      * (b) the tags associated with the event's item, and (c) the
  2789.      * tag "all".  If there are a lot of tags then malloc an array
  2790.      * to hold all of the objects.
  2791.      */
  2792.  
  2793.     numObjects = itemPtr->numTags + 2;
  2794.     if (numObjects <= NUM_STATIC) {
  2795.     objectPtr = staticObjects;
  2796.     } else {
  2797.     objectPtr = (ClientData *) ckalloc((unsigned)
  2798.         (numObjects * sizeof(ClientData)));
  2799.     }
  2800.     objectPtr[0] = (ClientData) itemPtr;
  2801.     for (i = itemPtr->numTags-1; i >= 0; i--) {
  2802.     objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
  2803.     }
  2804.     objectPtr[itemPtr->numTags+1] = (ClientData) allUid;
  2805.  
  2806.     /*
  2807.      * Invoke the binding system, then free up the object array if
  2808.      * it was malloc-ed.
  2809.      */
  2810.  
  2811.     Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
  2812.         numObjects, objectPtr);
  2813.     if (objectPtr != staticObjects) {
  2814.     ckfree((char *) objectPtr);
  2815.     }
  2816. }
  2817.  
  2818. /*
  2819.  *----------------------------------------------------------------------
  2820.  *
  2821.  * CanvasBlinkProc --
  2822.  *
  2823.  *    This procedure is called as a timer handler to blink the
  2824.  *    insertion cursor off and on.
  2825.  *
  2826.  * Results:
  2827.  *    None.
  2828.  *
  2829.  * Side effects:
  2830.  *    The cursor gets turned on or off, redisplay gets invoked,
  2831.  *    and this procedure reschedules itself.
  2832.  *
  2833.  *----------------------------------------------------------------------
  2834.  */
  2835.  
  2836. static void
  2837. CanvasBlinkProc(clientData)
  2838.     ClientData clientData;    /* Pointer to record describing entry. */
  2839. {
  2840.     register Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  2841.  
  2842.     if (!(canvasPtr->flags & GOT_FOCUS) || (canvasPtr->cursorOffTime == 0)) {
  2843.     return;
  2844.     }
  2845.     if (canvasPtr->flags & CURSOR_ON) {
  2846.     canvasPtr->flags &= ~CURSOR_ON;
  2847.     canvasPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
  2848.         canvasPtr->cursorOffTime, CanvasBlinkProc,
  2849.         (ClientData) canvasPtr);
  2850.     } else {
  2851.     canvasPtr->flags |= CURSOR_ON;
  2852.     canvasPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
  2853.         canvasPtr->cursorOnTime, CanvasBlinkProc,
  2854.         (ClientData) canvasPtr);
  2855.     }
  2856.     if (canvasPtr->focusItemPtr != NULL) {
  2857.     EventuallyRedrawArea(canvasPtr, canvasPtr->focusItemPtr->x1,
  2858.         canvasPtr->focusItemPtr->y1, canvasPtr->focusItemPtr->x2,
  2859.         canvasPtr->focusItemPtr->y2);
  2860.     }
  2861. }
  2862.  
  2863. /*
  2864.  *----------------------------------------------------------------------
  2865.  *
  2866.  * CanvasFocusProc --
  2867.  *
  2868.  *    This procedure is called whenever a canvas gets or loses the
  2869.  *    input focus.  It's also called whenever the window is
  2870.  *    reconfigured while it has the focus.
  2871.  *
  2872.  * Results:
  2873.  *    None.
  2874.  *
  2875.  * Side effects:
  2876.  *    The cursor gets turned on or off.
  2877.  *
  2878.  *----------------------------------------------------------------------
  2879.  */
  2880.  
  2881. static void
  2882. CanvasFocusProc(clientData, gotFocus)
  2883.     ClientData clientData;    /* Pointer to structure describing entry. */
  2884.     int gotFocus;        /* 1 means window is getting focus, 0 means
  2885.                  * it's losing it. */
  2886. {
  2887.     register Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  2888.  
  2889.     Tk_DeleteTimerHandler(canvasPtr->cursorBlinkHandler);
  2890.     if (gotFocus) {
  2891.     canvasPtr->flags |= GOT_FOCUS | CURSOR_ON;
  2892.     if (canvasPtr->cursorOffTime != 0) {
  2893.         canvasPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
  2894.             canvasPtr->cursorOnTime, CanvasBlinkProc,
  2895.             (ClientData) canvasPtr);
  2896.     }
  2897.     } else {
  2898.     canvasPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
  2899.     canvasPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
  2900.     }
  2901.     if (canvasPtr->focusItemPtr != NULL) {
  2902.     EventuallyRedrawArea(canvasPtr, canvasPtr->focusItemPtr->x1,
  2903.         canvasPtr->focusItemPtr->y1, canvasPtr->focusItemPtr->x2,
  2904.         canvasPtr->focusItemPtr->y2);
  2905.     }
  2906. }
  2907.  
  2908. /*
  2909.  *----------------------------------------------------------------------
  2910.  *
  2911.  * CanvasSelectTo --
  2912.  *
  2913.  *    Modify the selection by moving its un-anchored end.  This could
  2914.  *    make the selection either larger or smaller.
  2915.  *
  2916.  * Results:
  2917.  *    None.
  2918.  *
  2919.  * Side effects:
  2920.  *    The selection changes.
  2921.  *
  2922.  *----------------------------------------------------------------------
  2923.  */
  2924.  
  2925. static void
  2926. CanvasSelectTo(canvasPtr, itemPtr, index)
  2927.     register Tk_Canvas *canvasPtr;        /* Information about widget. */
  2928.     register Tk_Item *itemPtr;        /* Item that is to hold selection. */
  2929.     int index;                /* Index of element that is to
  2930.                      * become the "other" end of the
  2931.                      * selection. */
  2932. {
  2933.     int oldFirst, oldLast;
  2934.     Tk_Item *oldSelPtr;
  2935.  
  2936.     oldFirst = canvasPtr->selectFirst;
  2937.     oldLast = canvasPtr->selectLast;
  2938.     oldSelPtr = canvasPtr->selItemPtr;
  2939.  
  2940.     /*
  2941.      * Grab the selection if we don't own it already.
  2942.      */
  2943.  
  2944.     if (canvasPtr->selItemPtr == NULL) {
  2945.     Tk_OwnSelection(canvasPtr->tkwin, CanvasLostSelection,
  2946.         (ClientData) canvasPtr);
  2947.     } else if (canvasPtr->selItemPtr != itemPtr) {
  2948.     EventuallyRedrawArea(canvasPtr, canvasPtr->selItemPtr->x1,
  2949.         canvasPtr->selItemPtr->y1, canvasPtr->selItemPtr->x2,
  2950.         canvasPtr->selItemPtr->y2);
  2951.     }
  2952.     canvasPtr->selItemPtr = itemPtr;
  2953.  
  2954.     if (canvasPtr->anchorItemPtr != itemPtr) {
  2955.     canvasPtr->anchorItemPtr = itemPtr;
  2956.     canvasPtr->selectAnchor = index;
  2957.     }
  2958.     if (canvasPtr->selectAnchor <= index) {
  2959.     canvasPtr->selectFirst = canvasPtr->selectAnchor;
  2960.     canvasPtr->selectLast = index;
  2961.     } else {
  2962.     canvasPtr->selectFirst = index;
  2963.     canvasPtr->selectLast = canvasPtr->selectAnchor - 1;
  2964.     }
  2965.     if ((canvasPtr->selectFirst != oldFirst)
  2966.         || (canvasPtr->selectLast != oldLast)
  2967.         || (itemPtr != oldSelPtr)) {
  2968.     EventuallyRedrawArea(canvasPtr, itemPtr->x1, itemPtr->y1,
  2969.         itemPtr->x2, itemPtr->y2);
  2970.     }
  2971. }
  2972.  
  2973. /*
  2974.  *--------------------------------------------------------------
  2975.  *
  2976.  * CanvasFetchSelection --
  2977.  *
  2978.  *    This procedure is invoked by Tk to return part or all of
  2979.  *    the selection, when the selection is in a canvas widget.
  2980.  *    This procedure always returns the selection as a STRING.
  2981.  *
  2982.  * Results:
  2983.  *    The return value is the number of non-NULL bytes stored
  2984.  *    at buffer.  Buffer is filled (or partially filled) with a
  2985.  *    NULL-terminated string containing part or all of the selection,
  2986.  *    as given by offset and maxBytes.
  2987.  *
  2988.  * Side effects:
  2989.  *    None.
  2990.  *
  2991.  *--------------------------------------------------------------
  2992.  */
  2993.  
  2994. static int
  2995. CanvasFetchSelection(clientData, offset, buffer, maxBytes)
  2996.     ClientData clientData;        /* Information about canvas widget. */
  2997.     int offset;                /* Offset within selection of first
  2998.                      * character to be returned. */
  2999.     char *buffer;            /* Location in which to place
  3000.                      * selection. */
  3001.     int maxBytes;            /* Maximum number of bytes to place
  3002.                      * at buffer, not including terminating
  3003.                      * NULL character. */
  3004. {
  3005.     register Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  3006.  
  3007.     if (canvasPtr->selItemPtr == NULL) {
  3008.     return -1;
  3009.     }
  3010.     if (canvasPtr->selItemPtr->typePtr->selectionProc == NULL) {
  3011.     return -1;
  3012.     }
  3013.     return (*canvasPtr->selItemPtr->typePtr->selectionProc)(
  3014.         canvasPtr, canvasPtr->selItemPtr, offset, buffer, maxBytes);
  3015. }
  3016.  
  3017. /*
  3018.  *----------------------------------------------------------------------
  3019.  *
  3020.  * CanvasLostSelection --
  3021.  *
  3022.  *    This procedure is called back by Tk when the selection is
  3023.  *    grabbed away from a canvas widget.
  3024.  *
  3025.  * Results:
  3026.  *    None.
  3027.  *
  3028.  * Side effects:
  3029.  *    The existing selection is unhighlighted, and the window is
  3030.  *    marked as not containing a selection.
  3031.  *
  3032.  *----------------------------------------------------------------------
  3033.  */
  3034.  
  3035. static void
  3036. CanvasLostSelection(clientData)
  3037.     ClientData clientData;        /* Information about entry widget. */
  3038. {
  3039.     Tk_Canvas *canvasPtr = (Tk_Canvas *) clientData;
  3040.  
  3041.     if (canvasPtr->selItemPtr != NULL) {
  3042.     EventuallyRedrawArea(canvasPtr, canvasPtr->selItemPtr->x1,
  3043.         canvasPtr->selItemPtr->y1, canvasPtr->selItemPtr->x2,
  3044.         canvasPtr->selItemPtr->y2);
  3045.     }
  3046.     canvasPtr->selItemPtr = NULL;
  3047. }
  3048.  
  3049. /*
  3050.  *--------------------------------------------------------------
  3051.  *
  3052.  * TkGetCanvasCoord --
  3053.  *
  3054.  *    Given a string, returns a floating-point canvas coordinate
  3055.  *    corresponding to that string.
  3056.  *
  3057.  * Results:
  3058.  *    The return value is a standard Tcl return result.  If
  3059.  *    TCL_OK is returned, then everything went well and the
  3060.  *    canvas coordinate is stored at *doublePtr;  otherwise
  3061.  *    TCL_ERROR is returned and an error message is left in
  3062.  *    canvasPtr->interp->result.
  3063.  *
  3064.  * Side effects:
  3065.  *    None.
  3066.  *
  3067.  *--------------------------------------------------------------
  3068.  */
  3069.  
  3070. int
  3071. TkGetCanvasCoord(canvasPtr, string, doublePtr)
  3072.     Tk_Canvas *canvasPtr;    /* Canvas to which coordinate applies. */
  3073.     char *string;        /* Describes coordinate (any screen
  3074.                  * coordinate form may be used here). */
  3075.     double *doublePtr;        /* Place to store converted coordinate. */
  3076. {
  3077.     if (Tk_GetScreenMM(canvasPtr->interp, canvasPtr->tkwin, string,
  3078.         doublePtr) != TCL_OK) {
  3079.     return TCL_ERROR;
  3080.     }
  3081.     *doublePtr *= canvasPtr->pixelsPerMM;
  3082.     return TCL_OK;
  3083. }
  3084.  
  3085. /*
  3086.  *--------------------------------------------------------------
  3087.  *
  3088.  * GridAlign --
  3089.  *
  3090.  *    Given a coordinate and a grid spacing, this procedure
  3091.  *    computes the location of the nearest grid line to the
  3092.  *    coordinate.
  3093.  *
  3094.  * Results:
  3095.  *    The return value is the location of the grid line nearest
  3096.  *    to coord.
  3097.  *
  3098.  * Side effects:
  3099.  *    None.
  3100.  *
  3101.  *--------------------------------------------------------------
  3102.  */
  3103.  
  3104. static double
  3105. GridAlign(coord, spacing)
  3106.     double coord;        /* Coordinate to grid-align. */
  3107.     double spacing;        /* Spacing between grid lines.   If <= 0
  3108.                  * then no alignment is done. */
  3109. {
  3110.     if (spacing <= 0.0) {
  3111.     return coord;
  3112.     }
  3113.     if (coord < 0) {
  3114.     return -((int) ((-coord)/spacing + 0.5)) * spacing;
  3115.     }
  3116.     return ((int) (coord/spacing + 0.5)) * spacing;
  3117. }
  3118.  
  3119. /*
  3120.  *--------------------------------------------------------------
  3121.  *
  3122.  * CanvasUpdateScrollbars --
  3123.  *
  3124.  *    This procedure is invoked whenever a canvas has changed in
  3125.  *    a way that requires scrollbars to be redisplayed (e.g. the
  3126.  *    view in the canvas has changed).
  3127.  *
  3128.  * Results:
  3129.  *    None.
  3130.  *
  3131.  * Side effects:
  3132.  *    If there are scrollbars associated with the canvas, then
  3133.  *    their scrolling commands are invoked to cause them to
  3134.  *    redisplay.  If errors occur, additional Tcl commands may
  3135.  *    be invoked to process the errors.
  3136.  *
  3137.  *--------------------------------------------------------------
  3138.  */
  3139.  
  3140. static void
  3141. CanvasUpdateScrollbars(canvasPtr)
  3142.     register Tk_Canvas *canvasPtr;        /* Information about canvas. */
  3143. {
  3144.     int result, size, first, last, page;
  3145.     char args[200];
  3146.  
  3147. #define ROUND(number)                        \
  3148.     if (number >= 0) {                        \
  3149.     number = (number + canvasPtr->scrollIncrement/2)    \
  3150.         /canvasPtr->scrollIncrement;            \
  3151.     } else {                            \
  3152.     number = -(((-number) + canvasPtr->scrollIncrement/2)    \
  3153.         /canvasPtr->scrollIncrement);            \
  3154.     }
  3155.  
  3156.     canvasPtr->flags &= ~UPDATE_SCROLLBARS;
  3157.     if (canvasPtr->xScrollCmd != NULL) {
  3158.     size = ((canvasPtr->scrollX2 - canvasPtr->scrollX1)
  3159.         /canvasPtr->scrollIncrement) + 1;
  3160.     first = canvasPtr->xOrigin - canvasPtr->scrollX1;
  3161.     ROUND(first);
  3162.     last = canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
  3163.         - 1 - canvasPtr->scrollX1;
  3164.     ROUND(last);
  3165.     page = last - first - 1;
  3166.     if (page <= 0) {
  3167.         page = 1;
  3168.     }
  3169.     sprintf(args, " %d %d %d %d", size, page, first, last);
  3170.     result = Tcl_VarEval(canvasPtr->interp, canvasPtr->xScrollCmd, args,
  3171.         (char *) NULL);
  3172.     if (result != TCL_OK) {
  3173.         TkBindError(canvasPtr->interp);
  3174.     }
  3175.     Tcl_ResetResult(canvasPtr->interp);
  3176.     }
  3177.  
  3178.     if (canvasPtr->yScrollCmd != NULL) {
  3179.     size = ((canvasPtr->scrollY2 - canvasPtr->scrollY1)
  3180.         /canvasPtr->scrollIncrement) + 1;
  3181.     first = canvasPtr->yOrigin - canvasPtr->scrollY1;
  3182.     ROUND(first);
  3183.     last = canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
  3184.         - 1 - canvasPtr->scrollY1;
  3185.     ROUND(last);
  3186.     page = last - first - 1;
  3187.     if (page <= 0) {
  3188.         page = 1;
  3189.     }
  3190.     sprintf(args, " %d %d %d %d", size, page, first, last);
  3191.     result = Tcl_VarEval(canvasPtr->interp, canvasPtr->yScrollCmd, args,
  3192.         (char *) NULL);
  3193.     if (result != TCL_OK) {
  3194.         TkBindError(canvasPtr->interp);
  3195.     }
  3196.     Tcl_ResetResult(canvasPtr->interp);
  3197.     }
  3198. }
  3199.  
  3200. /*
  3201.  *--------------------------------------------------------------
  3202.  *
  3203.  * CanvasSetOrigin --
  3204.  *
  3205.  *    This procedure is invoked to change the mapping between
  3206.  *    canvas coordinates and screen coordinates in the canvas
  3207.  *    window.
  3208.  *
  3209.  * Results:
  3210.  *    None.
  3211.  *
  3212.  * Side effects:
  3213.  *    The canvas will be redisplayed to reflect the change in
  3214.  *    view.  In addition, scrollbars will be updated if there
  3215.  *    are any.
  3216.  *
  3217.  *--------------------------------------------------------------
  3218.  */
  3219.  
  3220. static void
  3221. CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
  3222.     register Tk_Canvas *canvasPtr;    /* Information about canvas. */
  3223.     int xOrigin;            /* New X origin for canvas (canvas
  3224.                      * x-coord corresponding to left edge
  3225.                      * of canvas window). */
  3226.     int yOrigin;            /* New Y origin for canvas (canvas
  3227.                      * y-coord corresponding to top edge
  3228.                      * of canvas window). */
  3229. {
  3230.     /*
  3231.      * Adjust the origin if necessary to keep as much as possible of the
  3232.      * canvas in the view.
  3233.      */
  3234.  
  3235.     if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
  3236.     int windowWidth, windowHeight, canvasWidth, canvasHeight;
  3237.  
  3238.     windowWidth = Tk_Width(canvasPtr->tkwin);
  3239.     windowHeight = Tk_Height(canvasPtr->tkwin);
  3240.     canvasWidth = canvasPtr->scrollX2 - canvasPtr->scrollX1;
  3241.     canvasHeight = canvasPtr->scrollY2 - canvasPtr->scrollY1;
  3242.     if (canvasWidth < windowWidth) {
  3243.         xOrigin = (canvasPtr->scrollX1) - (windowWidth-canvasWidth)/2;
  3244.     } else if (xOrigin < canvasPtr->scrollX1) {
  3245.         xOrigin = canvasPtr->scrollX1;
  3246.     } else if (xOrigin > (canvasPtr->scrollX2 - windowWidth)) {
  3247.         xOrigin = canvasPtr->scrollX2 - windowWidth;
  3248.     }
  3249.     if (canvasHeight < windowHeight) {
  3250.         yOrigin = (canvasPtr->scrollY1) - (windowHeight-canvasHeight)/2;
  3251.     } else if (yOrigin < canvasPtr->scrollY1) {
  3252.         yOrigin = canvasPtr->scrollY1;
  3253.     } else if (yOrigin > (canvasPtr->scrollY2 - windowHeight)) {
  3254.         yOrigin = canvasPtr->scrollY2 - windowHeight;
  3255.     }
  3256.     }
  3257.  
  3258.     if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
  3259.     return;
  3260.     }
  3261.  
  3262.     /*
  3263.      * Tricky point:  must redisplay not only everything that's visible
  3264.      * in the window's final configuration, but also everything that was
  3265.      * visible in the initial configuration.  This is needed because some
  3266.      * item types, like windows, need to know when they move off-screen
  3267.      * so they can explicitly undisplay themselves.
  3268.      */
  3269.  
  3270.     EventuallyRedrawArea(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin,
  3271.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  3272.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  3273.     canvasPtr->xOrigin = xOrigin;
  3274.     canvasPtr->yOrigin = yOrigin;
  3275.     canvasPtr->flags |= UPDATE_SCROLLBARS;
  3276.     EventuallyRedrawArea(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin,
  3277.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  3278.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  3279. }
  3280.  
  3281. /*
  3282.  *--------------------------------------------------------------
  3283.  *
  3284.  * CanvasTagsParseProc --
  3285.  *
  3286.  *    This procedure is invoked during option processing to handle
  3287.  *    "-tags" options for canvas items.
  3288.  *
  3289.  * Results:
  3290.  *    A standard Tcl return value.
  3291.  *
  3292.  * Side effects:
  3293.  *    The tags for a given item get replaced by those indicated
  3294.  *    in the value argument.
  3295.  *
  3296.  *--------------------------------------------------------------
  3297.  */
  3298.  
  3299.     /* ARGSUSED */
  3300. static int
  3301. CanvasTagsParseProc(clientData, interp, tkwin, value, widgRec, offset)
  3302.     ClientData clientData;        /* Not used.*/
  3303.     Tcl_Interp *interp;            /* Used for reporting errors. */
  3304.     Tk_Window tkwin;            /* Window containing canvas widget. */
  3305.     char *value;            /* Value of option (list of tag
  3306.                      * names). */
  3307.     char *widgRec;            /* Pointer to record for item. */
  3308.     int offset;                /* Offset into item (ignored). */
  3309. {
  3310.     register Tk_Item *itemPtr = (Tk_Item *) widgRec;
  3311.     int argc, i;
  3312.     char **argv;
  3313.     Tk_Uid *newPtr;
  3314.  
  3315.     /*
  3316.      * Break the value up into the individual tag names.
  3317.      */
  3318.  
  3319.     if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
  3320.     return TCL_ERROR;
  3321.     }
  3322.  
  3323.     /*
  3324.      * Make sure that there's enough space in the item to hold the
  3325.      * tag names.
  3326.      */
  3327.  
  3328.     if (itemPtr->tagSpace < argc) {
  3329.     newPtr = (Tk_Uid *) ckalloc((unsigned) (argc * sizeof(Tk_Uid)));
  3330.     for (i = itemPtr->numTags-1; i >= 0; i--) {
  3331.         newPtr[i] = itemPtr->tagPtr[i];
  3332.     }
  3333.     if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  3334.         ckfree((char *) itemPtr->tagPtr);
  3335.     }
  3336.     itemPtr->tagPtr = newPtr;
  3337.     itemPtr->tagSpace = argc;
  3338.     }
  3339.     itemPtr->numTags = argc;
  3340.     for (i = 0; i < argc; i++) {
  3341.     itemPtr->tagPtr[i] = Tk_GetUid(argv[i]);
  3342.     }
  3343.     ckfree((char *) argv);
  3344.     return TCL_OK;
  3345. }
  3346.  
  3347. /*
  3348.  *--------------------------------------------------------------
  3349.  *
  3350.  * CanvasTagsPrintProc --
  3351.  *
  3352.  *    This procedure is invoked by the Tk configuration code
  3353.  *    to produce a printable string for the "-tags" configuration
  3354.  *    option for canvas items.
  3355.  *
  3356.  * Results:
  3357.  *    The return value is a string describing all the tags for
  3358.  *    the item referred to by "widgRec".  In addition, *freeProcPtr
  3359.  *    is filled in with the address of a procedure to call to free
  3360.  *    the result string when it's no longer needed (or NULL to
  3361.  *    indicate that the string doesn't need to be freed).
  3362.  *
  3363.  * Side effects:
  3364.  *    None.
  3365.  *
  3366.  *--------------------------------------------------------------
  3367.  */
  3368.  
  3369.     /* ARGSUSED */
  3370. static char *
  3371. CanvasTagsPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
  3372.     ClientData clientData;        /* Ignored. */
  3373.     Tk_Window tkwin;            /* Window containing canvas widget. */
  3374.     char *widgRec;            /* Pointer to record for item. */
  3375.     int offset;                /* Ignored. */
  3376.     Tcl_FreeProc **freeProcPtr;        /* Pointer to variable to fill in with
  3377.                      * information about how to reclaim
  3378.                      * storage for return string. */
  3379. {
  3380.     register Tk_Item *itemPtr = (Tk_Item *) widgRec;
  3381.  
  3382.     if (itemPtr->numTags == 0) {
  3383.     *freeProcPtr = (Tcl_FreeProc *) NULL;
  3384.     return "";
  3385.     }
  3386.     if (itemPtr->numTags == 1) {
  3387.     *freeProcPtr = (Tcl_FreeProc *) NULL;
  3388.     return (char *) itemPtr->tagPtr[0];
  3389.     }
  3390.     *freeProcPtr = (Tcl_FreeProc *) free;
  3391.     return Tcl_Merge(itemPtr->numTags, (char **) itemPtr->tagPtr);
  3392. }
  3393.